Skip to content

Commit bca75ce

Browse files
authored
Merge branch 'main' into feature/iac-scaffolding
2 parents 2f44956 + 71e1997 commit bca75ce

File tree

26 files changed

+3676
-2806
lines changed

26 files changed

+3676
-2806
lines changed

.github/workflows/run-ci-cd.yaml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ jobs:
276276
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
277277

278278
- name: Login to Docker Hub
279-
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1
279+
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef
280280
with:
281281
username: ${{ secrets.DOCKERHUB_USERNAME }}
282282
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -377,16 +377,18 @@ jobs:
377377
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
378378

379379
- name: Scan backend image
380+
continue-on-error: true
380381
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8
381382
with:
382-
exit-code: 0
383+
exit-code: 1
383384
image-ref: owasp/nest:backend-staging
384385
scan-type: image
385386
trivy-config: trivy.yaml
386387
trivyignores: trivyignore.yaml
387388
version: latest
388389

389390
- name: Scan frontend image
391+
continue-on-error: true
390392
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8
391393
with:
392394
exit-code: 1
@@ -600,7 +602,7 @@ jobs:
600602
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
601603

602604
- name: Login to Docker Hub
603-
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1
605+
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef
604606
with:
605607
username: ${{ secrets.DOCKERHUB_USERNAME }}
606608
password: ${{ secrets.DOCKERHUB_TOKEN }}

.github/workflows/run-code-ql.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
3232

3333
- name: Initialize CodeQL
34-
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3
34+
uses: github/codeql-action/init@3599b3baa15b485a2e49ef411a7a4bb2452e7f93
3535
with:
3636
languages: ${{ matrix.language }}
3737

@@ -55,6 +55,6 @@ jobs:
5555
run: pnpm install --frozen-lockfile
5656

5757
- name: Perform CodeQL analysis
58-
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3
58+
uses: github/codeql-action/analyze@3599b3baa15b485a2e49ef411a7a4bb2452e7f93
5959
with:
6060
category: /language:${{ matrix.language }}

.github/workflows/update-nest-test-images.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
2424

2525
- name: Login to Docker Hub
26-
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1
26+
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef
2727
with:
2828
username: ${{ secrets.DOCKERHUB_USERNAME }}
2929
password: ${{ secrets.DOCKERHUB_TOKEN }}

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ repos:
1010
exclude: (.github|pnpm-lock.yaml)
1111

1212
- repo: https://github.com/astral-sh/ruff-pre-commit
13-
rev: v0.13.1
13+
rev: v0.13.2
1414
hooks:
1515
- id: ruff
1616
args:
@@ -83,7 +83,7 @@ repos:
8383
exclude: pnpm-lock.yaml
8484

8585
- repo: https://github.com/tox-dev/pyproject-fmt
86-
rev: v2.6.0
86+
rev: v2.7.0
8787
hooks:
8888
- id: pyproject-fmt
8989

backend/apps/owasp/admin/chapter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from apps.owasp.models.chapter import Chapter
66

7-
from .mixins import EntityMemberInline, GenericEntityAdminMixin
7+
from .mixins import EntityChannelInline, EntityMemberInline, GenericEntityAdminMixin
88

99

1010
class ChapterAdmin(admin.ModelAdmin, GenericEntityAdminMixin):
@@ -15,7 +15,7 @@ class ChapterAdmin(admin.ModelAdmin, GenericEntityAdminMixin):
1515
"leaders",
1616
"suggested_leaders",
1717
)
18-
inlines = (EntityMemberInline,)
18+
inlines = (EntityMemberInline, EntityChannelInline)
1919
list_display = (
2020
"name",
2121
"created_at",

backend/apps/owasp/admin/committee.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
from django.contrib import admin
44

5+
from apps.owasp.admin.mixins import (
6+
EntityChannelInline,
7+
EntityMemberInline,
8+
GenericEntityAdminMixin,
9+
)
510
from apps.owasp.models.committee import Committee
611

7-
from .mixins import EntityMemberInline, GenericEntityAdminMixin
8-
912

1013
class CommitteeAdmin(admin.ModelAdmin, GenericEntityAdminMixin):
1114
"""Admin for Committee model."""
@@ -15,7 +18,7 @@ class CommitteeAdmin(admin.ModelAdmin, GenericEntityAdminMixin):
1518
"leaders",
1619
"suggested_leaders",
1720
)
18-
inlines = (EntityMemberInline,)
21+
inlines = (EntityMemberInline, EntityChannelInline)
1922
search_fields = ("name",)
2023

2124

backend/apps/owasp/admin/mixins.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
"""OWASP admin mixins for common functionality."""
22

33
from django.contrib.contenttypes.admin import GenericTabularInline
4+
from django.contrib.contenttypes.models import ContentType
45
from django.utils.html import escape
56
from django.utils.safestring import mark_safe
67

8+
from apps.owasp.admin.widgets import ChannelIdWidget
9+
from apps.owasp.models.entity_channel import EntityChannel
710
from apps.owasp.models.entity_member import EntityMember
11+
from apps.slack.models.conversation import Conversation
812

913

1014
class BaseOwaspAdminMixin:
@@ -58,6 +62,36 @@ class EntityMemberInline(GenericTabularInline):
5862
raw_id_fields = ("member",)
5963

6064

65+
class EntityChannelInline(GenericTabularInline):
66+
"""EntityChannel inline for admin."""
67+
68+
ct_field = "entity_type"
69+
ct_fk_field = "entity_id"
70+
extra = 1
71+
fields = (
72+
"channel_type",
73+
"channel_id",
74+
"platform",
75+
"is_default",
76+
"is_active",
77+
"is_reviewed",
78+
)
79+
model = EntityChannel
80+
ordering = ("platform", "channel_id")
81+
82+
def formfield_for_dbfield(self, db_field, request, **kwargs):
83+
"""Override to add custom widget for channel_id field and limit channel_type options."""
84+
if db_field.name == "channel_id":
85+
kwargs["widget"] = ChannelIdWidget()
86+
elif db_field.name == "channel_type":
87+
# Limit channel_type to only Conversation (Slack channels)
88+
conversation_ct = ContentType.objects.get_for_model(Conversation)
89+
kwargs["queryset"] = ContentType.objects.filter(id=conversation_ct.id)
90+
kwargs["initial"] = conversation_ct
91+
92+
return super().formfield_for_dbfield(db_field, request, **kwargs)
93+
94+
6195
class GenericEntityAdminMixin(BaseOwaspAdminMixin):
6296
"""Mixin for generic entity admin with common entity functionality."""
6397

backend/apps/owasp/admin/project.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
from django.contrib import admin
44

5+
from apps.owasp.admin.mixins import (
6+
EntityChannelInline,
7+
EntityMemberInline,
8+
GenericEntityAdminMixin,
9+
)
510
from apps.owasp.models.project import Project
611

7-
from .mixins import EntityMemberInline, GenericEntityAdminMixin
8-
912

1013
class ProjectAdmin(admin.ModelAdmin, GenericEntityAdminMixin):
1114
"""Admin for Project model."""
@@ -20,7 +23,7 @@ class ProjectAdmin(admin.ModelAdmin, GenericEntityAdminMixin):
2023
"leaders",
2124
"suggested_leaders",
2225
)
23-
inlines = (EntityMemberInline,)
26+
inlines = (EntityMemberInline, EntityChannelInline)
2427
list_display = (
2528
"custom_field_name",
2629
"created_at",
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""Custom widgets for OWASP admin."""
2+
3+
from django import forms
4+
from django.utils.safestring import mark_safe
5+
6+
7+
class ChannelIdWidget(forms.TextInput):
8+
"""Custom widget for channel_id with search functionality."""
9+
10+
def __init__(self, *args, **kwargs):
11+
"""Initialize the widget with custom attributes."""
12+
super().__init__(*args, **kwargs)
13+
self.attrs.update({"class": "vForeignKeyRawIdAdminField", "placeholder": "Channel ID"})
14+
15+
def render(self, name, value, attrs=None, renderer=None):
16+
"""Render the widget with a search button."""
17+
widget_html = super().render(name, value, attrs, renderer)
18+
19+
search_button = (
20+
"<a href='/a/slack/conversation/' class='related-lookup'"
21+
f"id='lookup_id_{name}' title='Look up related objects'></a>"
22+
)
23+
24+
return mark_safe(f"{widget_html}{search_button}") # noqa: S308
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Generated by Django 5.2.6 on 2025-09-30 23:14
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
dependencies = [
9+
("contenttypes", "0002_remove_content_type_name"),
10+
("owasp", "0052_remove_entitymember_owasp_entit_member__6e516f_idx_and_more"),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name="EntityChannel",
16+
fields=[
17+
(
18+
"id",
19+
models.BigAutoField(
20+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
21+
),
22+
),
23+
("channel_id", models.PositiveBigIntegerField()),
24+
("entity_id", models.PositiveBigIntegerField()),
25+
(
26+
"is_active",
27+
models.BooleanField(
28+
default=True, help_text="Indicates if this channel is active"
29+
),
30+
),
31+
(
32+
"is_default",
33+
models.BooleanField(
34+
default=False,
35+
help_text="Indicates if this is the main channel for the entity",
36+
),
37+
),
38+
(
39+
"is_reviewed",
40+
models.BooleanField(
41+
default=False, help_text="Indicates if the channel has been reviewed"
42+
),
43+
),
44+
(
45+
"platform",
46+
models.CharField(
47+
choices=[("slack", "Slack")],
48+
default="slack",
49+
help_text="Platform of the channel (e.g., Slack)",
50+
max_length=5,
51+
),
52+
),
53+
(
54+
"channel_type",
55+
models.ForeignKey(
56+
on_delete=django.db.models.deletion.CASCADE,
57+
related_name="+",
58+
to="contenttypes.contenttype",
59+
),
60+
),
61+
(
62+
"entity_type",
63+
models.ForeignKey(
64+
on_delete=django.db.models.deletion.CASCADE,
65+
related_name="+",
66+
to="contenttypes.contenttype",
67+
),
68+
),
69+
],
70+
options={
71+
"verbose_name": "Entity channel",
72+
"verbose_name_plural": "Entity channels",
73+
"db_table": "owasp_entity_channels",
74+
"unique_together": {("channel_id", "channel_type", "entity_id", "entity_type")},
75+
},
76+
),
77+
]

0 commit comments

Comments
 (0)