Skip to content

Commit 85eb8dd

Browse files
committed
Merge branch 'dev' of https://github.com/MaibornWolff/SecObserve into stackable
2 parents 70a443d + 882cdc1 commit 85eb8dd

File tree

64 files changed

+1348
-1132
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1348
-1132
lines changed

.github/workflows/scan_sca_current.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
name: Checkout
1717
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
1818
with:
19-
ref: 'v1.40.0'
19+
ref: 'v1.41.0'
2020
-
2121
name: Run SCA vulnerability scanners
2222
uses: MaibornWolff/secobserve_actions_templates/actions/vulnerability_scanner@936a764a4e82cc89772941e082ba24c371c6ef90 # main

.github/workflows/scorecard.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,6 @@ jobs:
6767

6868
# Upload the results to GitHub's code scanning dashboard.
6969
- name: "Upload to code-scanning"
70-
uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
70+
uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
7171
with:
7272
sarif_file: results.sarif

backend/application/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.40.0"
1+
__version__ = "1.41.0"
22

33
import pymysql
44

backend/application/access_control/api/filters.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,9 @@ class ApiTokenFilter(FilterSet):
195195

196196
ordering = OrderingFilter(
197197
# tuple-mapping retains order
198-
fields=(("user__username", "name"),),
198+
fields=(("user__username", "name"), ("user", "user")),
199199
)
200200

201201
class Meta:
202202
model = API_Token
203-
fields = ["name"]
203+
fields = ["name", "user"]

backend/application/access_control/api/serializers.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import re
12
from typing import Any, Optional
23

34
from rest_framework.serializers import (
@@ -14,6 +15,7 @@
1415
Authorization_Group_Member,
1516
User,
1617
)
18+
from application.access_control.queries.api_token import get_api_tokens_for_user
1719
from application.access_control.queries.authorization_group_member import (
1820
get_authorization_group_member,
1921
)
@@ -111,6 +113,7 @@ class UserSerializer(UserListSerializer):
111113
has_authorization_groups = SerializerMethodField()
112114
has_product_group_members = SerializerMethodField()
113115
has_product_members = SerializerMethodField()
116+
has_api_tokens = SerializerMethodField()
114117

115118
class Meta:
116119
model = User
@@ -136,6 +139,7 @@ class Meta:
136139
"has_authorization_groups",
137140
"has_product_group_members",
138141
"has_product_members",
142+
"has_api_tokens",
139143
]
140144

141145
def to_representation(self, instance: User) -> dict[str, Any]:
@@ -146,6 +150,7 @@ def to_representation(self, instance: User) -> dict[str, Any]:
146150
data.pop("has_authorization_groups")
147151
data.pop("has_product_group_members")
148152
data.pop("has_product_members")
153+
data.pop("has_api_tokens")
149154

150155
return data
151156

@@ -161,6 +166,9 @@ def get_has_product_group_members(self, obj: User) -> bool:
161166
def get_has_product_members(self, obj: User) -> bool:
162167
return Product_Member.objects.filter(user=obj, product__is_product_group=False).exists()
163168

169+
def get_has_api_tokens(self, obj: User) -> bool:
170+
return get_api_tokens_for_user(obj).exists()
171+
164172

165173
class UserUpdateSerializer(ModelSerializer):
166174
class Meta:
@@ -297,15 +305,17 @@ def get_name(self, obj: API_Token) -> str:
297305
return obj.user.username
298306

299307
def get_product(self, obj: API_Token) -> Optional[int]:
300-
product_member = Product_Member.objects.filter(user=obj.user, product__is_product_group=False).first()
301-
if product_member:
302-
return product_member.product.pk
308+
if re.match("-product-\\d*-api_token-", obj.user.username):
309+
product_member = Product_Member.objects.filter(user=obj.user, product__is_product_group=False).first()
310+
if product_member:
311+
return product_member.product.pk
303312
return None
304313

305314
def get_product_group(self, obj: API_Token) -> Optional[int]:
306-
product_member = Product_Member.objects.filter(user=obj.user, product__is_product_group=True).first()
307-
if product_member:
308-
return product_member.product.pk
315+
if re.match("-product-\\d*-api_token-", obj.user.username):
316+
product_member = Product_Member.objects.filter(user=obj.user, product__is_product_group=True).first()
317+
if product_member:
318+
return product_member.product.pk
309319
return None
310320

311321

backend/application/access_control/api/views.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from rest_framework.exceptions import PermissionDenied, ValidationError
2222
from rest_framework.filters import SearchFilter
2323
from rest_framework.mixins import ListModelMixin
24-
from rest_framework.permissions import IsAdminUser, IsAuthenticated
24+
from rest_framework.permissions import IsAuthenticated
2525
from rest_framework.request import Request
2626
from rest_framework.response import Response
2727
from rest_framework.serializers import BaseSerializer
@@ -60,6 +60,7 @@
6060
JWT_Secret,
6161
User,
6262
)
63+
from application.access_control.queries.api_token import get_api_tokens
6364
from application.access_control.queries.authorization_group import (
6465
get_authorization_groups,
6566
)
@@ -249,8 +250,11 @@ def get_queryset(self) -> QuerySet[Authorization_Group_Member]:
249250
class ApiTokenViewSet(ListModelMixin, GenericViewSet):
250251
serializer_class = ApiTokenSerializer
251252
filterset_class = ApiTokenFilter
252-
permission_classes = (IsAuthenticated, IsAdminUser)
253-
queryset = API_Token.objects.all()
253+
permission_classes = (IsAuthenticated,)
254+
queryset = API_Token.objects.none()
255+
256+
def get_queryset(self) -> QuerySet[API_Token]:
257+
return get_api_tokens()
254258

255259

256260
class CreateUserAPITokenView(APIView):
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from django.db.models.query import QuerySet
2+
3+
from application.access_control.models import API_Token, User
4+
from application.access_control.services.current_user import get_current_user
5+
6+
7+
def get_api_tokens() -> QuerySet[API_Token]:
8+
user = get_current_user()
9+
10+
if user is None:
11+
return API_Token.objects.none()
12+
13+
api_tokens = API_Token.objects.all()
14+
15+
if user.is_superuser:
16+
return api_tokens
17+
18+
return api_tokens.filter(user=user)
19+
20+
21+
def get_api_tokens_for_user(given_user: User) -> QuerySet[API_Token]:
22+
current_user = get_current_user()
23+
24+
if current_user is None:
25+
return API_Token.objects.none()
26+
27+
api_tokens = API_Token.objects.filter(user=given_user)
28+
29+
if current_user.is_superuser:
30+
return api_tokens
31+
32+
return api_tokens if current_user == given_user else API_Token.objects.none()

backend/application/core/signals.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from django.dispatch import receiver
77
from huey.contrib.djhuey import db_task, lock_task
88

9+
from application.access_control.models import API_Token, User
910
from application.access_control.services.current_user import get_current_user
1011
from application.authorization.services.roles_permissions import Roles
1112
from application.commons.models import Settings
@@ -29,6 +30,18 @@ def observation_pre_save(sender: Any, instance: Observation, **kwargs: Any) -> N
2930
set_product_flags(instance)
3031

3132

33+
@receiver(post_delete, sender=Product)
34+
def product_post_delete(sender: Any, instance: Product, **kwargs: Any) -> None: # pylint: disable=unused-argument
35+
# sender is needed according to Django documentation
36+
try:
37+
user = User.objects.get(username=f"-product-{instance.pk}-api_token-")
38+
api_token = API_Token.objects.get(user=user)
39+
api_token.delete()
40+
user.delete()
41+
except (User.DoesNotExist, API_Token.DoesNotExist):
42+
pass
43+
44+
3245
@receiver(post_save, sender=Product)
3346
def product_post_save(
3447
sender: Any, instance: Product, created: bool, **kwargs: Any # pylint: disable=unused-argument

backend/application/import_observations/parsers/sarif/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def get_description( # pylint: disable=too-many-branches
236236
) -> str:
237237
description = ""
238238

239-
sarif_message_text = result.get("message", {}).get("text")
239+
sarif_message_text = result.get("message", {}).get("text", "")
240240
if sarif_message_text and not sarif_scanner.lower().startswith("trivy"):
241241
# Message text of Trivy has only redundant information
242242
description += f"{sarif_message_text}\n\n"

0 commit comments

Comments
 (0)