Skip to content

Commit

Permalink
Merge pull request #797 from MaibornWolff/dev
Browse files Browse the repository at this point in the history
chore: merge for release 1.1.0
  • Loading branch information
StefanFl authored Nov 26, 2023
2 parents 766ec0e + 5ca44b5 commit 32c47c4
Show file tree
Hide file tree
Showing 88 changed files with 2,731 additions and 794 deletions.
19 changes: 1 addition & 18 deletions .github/workflows/check_vulnerabilities.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,4 @@ jobs:
uses: MaibornWolff/secobserve_actions_templates/actions/vulnerability_scanner@main
with:
so_configuration: 'so_configuration.yml'

- name: Upload results
uses: actions/upload-artifact@v3
with:
name: secobserve
path: |
bandit_backend.sarif
checkov.sarif
eslint_frontend.sarif
gitleaks.sarif
grype_backend_image.json
grype_frontend_image.json
kics.sarif
semgrep_backend.sarif
semgrep_frontend.sarif
trivy_frontend_npm.json
trivy_backend_image.json
trivy_frontend_image.json
SO_API_TOKEN: ${{ secrets.SO_API_TOKEN }}
2 changes: 1 addition & 1 deletion .github/workflows/publish_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches:
- main
- feat/tfsec
- chore/doc-about

permissions:
contents: write
Expand Down
2 changes: 1 addition & 1 deletion backend/application/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.0.1"
__version__ = "1.1.0"
24 changes: 21 additions & 3 deletions backend/application/access_control/services/authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,22 @@
get_roles_with_permissions,
)
from application.commons.services.global_request import get_current_user
from application.core.models import Branch, Observation, Product, Product_Member
from application.core.models import (
Branch,
Observation,
Product,
Product_Member,
Service,
)
from application.core.queries.product_member import get_product_member
from application.import_observations.models import Api_Configuration
from application.import_observations.models import (
Api_Configuration,
Vulnerability_Check,
)
from application.rules.models import Rule


def user_has_permission( # pylint: disable=too-many-return-statements
def user_has_permission( # pylint: disable=too-many-return-statements,too-many-branches
obj, permission: int, user: User = None
) -> bool:
# There are a lot of different objects that need to be checked for permissions.
Expand Down Expand Up @@ -69,6 +78,9 @@ def user_has_permission( # pylint: disable=too-many-return-statements
if isinstance(obj, Branch) and permission in Permissions.get_branch_permissions():
return user_has_permission(obj.product, permission, user)

if isinstance(obj, Service) and permission in Permissions.get_service_permissions():
return user_has_permission(obj.product, permission, user)

if (
isinstance(obj, Observation)
and permission in Permissions.get_observation_permissions()
Expand All @@ -81,6 +93,12 @@ def user_has_permission( # pylint: disable=too-many-return-statements
):
return user_has_permission(obj.product, permission, user)

if (
isinstance(obj, Vulnerability_Check)
and permission in Permissions.get_vulnerability_check_permissions()
):
return user_has_permission(obj.product, permission, user)

raise NoAuthorizationImplementedError(
f"No authorization implemented for class {type(obj).__name__} and permission {permission}"
)
Expand Down
16 changes: 14 additions & 2 deletions backend/application/access_control/services/oidc_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,21 @@ def _validate_jwt(self, token: str) -> Optional[User]:
jwks_uri = self._get_jwks_uri()
jwks_client = jwt.PyJWKClient(jwks_uri)
signing_key = jwks_client.get_signing_key_from_jwt(token)
config = {"verify_aud": False}
options = {
"verify_signature": True,
"verify_aud": True,
"strict_aud": True,
"require": ["exp"],
"verify_iat": True,
"verify_exp": True,
"verify_nbf": True,
}
payload = jwt.decode(
jwt=token, key=signing_key.key, options=config, algorithms=ALGORITHMS
jwt=token,
options=options,
key=signing_key.key,
algorithms=ALGORITHMS,
audience=os.environ["OIDC_CLIENT_ID"],
)
username = payload.get(os.environ["OIDC_USERNAME"])
user = get_user_by_username(username)
Expand Down
22 changes: 22 additions & 0 deletions backend/application/access_control/services/roles_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class Permissions(IntEnum):
Branch_Delete = 1403
Branch_Create = 1404

Service_View = 1501
Service_Delete = 1503

Observation_View = 2001
Observation_Edit = 2002
Observation_Delete = 2003
Expand Down Expand Up @@ -113,6 +116,13 @@ def get_branch_permissions(cls):
Permissions.Branch_Create,
}

@classmethod
def get_service_permissions(cls):
return {
Permissions.Service_View,
Permissions.Service_Delete,
}

@classmethod
def get_api_configuration_permissions(cls):
return {
Expand All @@ -122,6 +132,12 @@ def get_api_configuration_permissions(cls):
Permissions.Api_Configuration_Create,
}

@classmethod
def get_vulnerability_check_permissions(cls):
return {
Permissions.Product_View,
}


def get_roles_with_permissions():
return {
Expand All @@ -131,6 +147,7 @@ def get_roles_with_permissions():
Permissions.Product_Member_View,
Permissions.Product_Rule_View,
Permissions.Branch_View,
Permissions.Service_View,
Permissions.Observation_View,
Permissions.Api_Configuration_View,
},
Expand All @@ -144,6 +161,7 @@ def get_roles_with_permissions():
Permissions.Product_Member_View,
Permissions.Product_Rule_View,
Permissions.Branch_View,
Permissions.Service_View,
Permissions.Observation_View,
Permissions.Observation_Edit,
Permissions.Observation_Create,
Expand All @@ -169,6 +187,8 @@ def get_roles_with_permissions():
Permissions.Branch_Edit,
Permissions.Branch_Delete,
Permissions.Branch_Create,
Permissions.Service_View,
Permissions.Service_Delete,
Permissions.Observation_View,
Permissions.Observation_Edit,
Permissions.Observation_Create,
Expand Down Expand Up @@ -199,6 +219,8 @@ def get_roles_with_permissions():
Permissions.Branch_Edit,
Permissions.Branch_Delete,
Permissions.Branch_Create,
Permissions.Service_View,
Permissions.Service_Delete,
Permissions.Observation_View,
Permissions.Observation_Edit,
Permissions.Observation_Create,
Expand Down
13 changes: 13 additions & 0 deletions backend/application/core/api/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
Parser,
Product,
Product_Member,
Service,
)

AGE_DAY = "Today"
Expand Down Expand Up @@ -123,6 +124,17 @@ class Meta:
fields = ["product", "name"]


class ServiceFilter(FilterSet):
ordering = OrderingFilter(
# tuple-mapping retains order
fields=(("name", "name")),
)

class Meta:
model = Service
fields = ["product", "name"]


class ParserFilter(FilterSet):
name = CharFilter(field_name="name", lookup_expr="icontains")
type = ChoiceFilter(field_name="type", choices=Parser.TYPE_CHOICES)
Expand Down Expand Up @@ -199,6 +211,7 @@ class Meta: # pylint: disable=duplicate-code
"scanner",
"upload_filename",
"api_configuration_name",
"origin_service",
]

def get_age(self, queryset, field_name, value): # pylint: disable=unused-argument
Expand Down
11 changes: 11 additions & 0 deletions backend/application/core/api/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ def has_object_permission(self, request, view, obj):
)


class UserHasServicePermission(BasePermission):
def has_object_permission(self, request, view, obj):
return check_object_permission(
request,
obj,
Permissions.Service_View,
None,
Permissions.Service_Delete,
)


class UserHasObservationPermission(BasePermission):
def has_permission(self, request, view):
return check_post_permission(
Expand Down
42 changes: 38 additions & 4 deletions backend/application/core/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
Product,
Product_Member,
Reference,
Service,
)
from application.core.queries.product_member import get_product_member
from application.core.services.observation_log import create_observation_log
Expand Down Expand Up @@ -335,6 +336,7 @@ def validate(self, attrs: dict):


class BranchSerializer(ModelSerializer):
name_with_product = SerializerMethodField()
is_default_branch = SerializerMethodField()
open_critical_observation_count = SerializerMethodField()
open_high_observation_count = SerializerMethodField()
Expand All @@ -347,6 +349,9 @@ class Meta:
model = Branch
fields = "__all__"

def get_name_with_product(self, obj: Service) -> str:
return f"{obj.name} ({obj.product.name})"

def get_is_default_branch(self, obj: Branch) -> bool:
return obj.product.repository_default_branch == obj

Expand All @@ -368,11 +373,40 @@ def get_open_none_observation_count(self, obj: Branch) -> int:
def get_open_unkown_observation_count(self, obj: Branch) -> int:
return obj.open_unkown_observation_count

def validate_product(self, product: Product) -> Product:
if product and product.is_product_group:
raise ValidationError("Product must not be a product group")

return product
class ServiceSerializer(ModelSerializer):
name_with_product = SerializerMethodField()
open_critical_observation_count = SerializerMethodField()
open_high_observation_count = SerializerMethodField()
open_medium_observation_count = SerializerMethodField()
open_low_observation_count = SerializerMethodField()
open_none_observation_count = SerializerMethodField()
open_unkown_observation_count = SerializerMethodField()

class Meta:
model = Service
fields = "__all__"

def get_name_with_product(self, obj: Service) -> str:
return f"{obj.name} ({obj.product.name})"

def get_open_critical_observation_count(self, obj: Service) -> int:
return obj.open_critical_observation_count

def get_open_high_observation_count(self, obj: Service) -> int:
return obj.open_high_observation_count

def get_open_medium_observation_count(self, obj: Service) -> int:
return obj.open_medium_observation_count

def get_open_low_observation_count(self, obj: Service) -> int:
return obj.open_low_observation_count

def get_open_none_observation_count(self, obj: Service) -> int:
return obj.open_none_observation_count

def get_open_unkown_observation_count(self, obj: Service) -> int:
return obj.open_unkown_observation_count


class ParserSerializer(ModelSerializer):
Expand Down
24 changes: 22 additions & 2 deletions backend/application/core/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from rest_framework.decorators import action
from rest_framework.exceptions import NotFound, ValidationError
from rest_framework.filters import SearchFilter
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.mixins import DestroyModelMixin, ListModelMixin, RetrieveModelMixin
from rest_framework.permissions import IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
Expand All @@ -27,12 +27,14 @@
ProductFilter,
ProductGroupFilter,
ProductMemberFilter,
ServiceFilter,
)
from application.core.api.permissions import (
UserHasBranchPermission,
UserHasObservationPermission,
UserHasProductMemberPermission,
UserHasProductPermission,
UserHasServicePermission,
)
from application.core.api.serializers import (
BranchSerializer,
Expand All @@ -49,6 +51,7 @@
ProductGroupSerializer,
ProductMemberSerializer,
ProductSerializer,
ServiceSerializer,
)
from application.core.models import (
Branch,
Expand All @@ -57,6 +60,7 @@
Parser,
Product,
Product_Member,
Service,
)
from application.core.queries.branch import get_branches
from application.core.queries.observation import (
Expand All @@ -66,6 +70,7 @@
)
from application.core.queries.product import get_product_by_id, get_products
from application.core.queries.product_member import get_product_members
from application.core.queries.service import get_services
from application.core.services.assessment import remove_assessment, save_assessment
from application.core.services.export_observations import (
export_observations_csv,
Expand Down Expand Up @@ -303,7 +308,8 @@ class BranchViewSet(ModelViewSet):
filterset_class = BranchFilter
permission_classes = (IsAuthenticated, UserHasBranchPermission)
queryset = Branch.objects.none()
filter_backends = [DjangoFilterBackend]
filter_backends = [SearchFilter, DjangoFilterBackend]
search_fields = ["name"]

def get_queryset(self):
return get_branches()
Expand All @@ -316,6 +322,20 @@ def destroy(self, request: Request, *args: Any, **kwargs: Any) -> Response:
return super().destroy(request, *args, **kwargs)


class ServiceViewSet(
GenericViewSet, ListModelMixin, RetrieveModelMixin, DestroyModelMixin
):
serializer_class = ServiceSerializer
filterset_class = ServiceFilter
permission_classes = (IsAuthenticated, UserHasServicePermission)
queryset = Service.objects.none()
filter_backends = [SearchFilter, DjangoFilterBackend]
search_fields = ["name"]

def get_queryset(self):
return get_services()


class ParserViewSet(GenericViewSet, ListModelMixin, RetrieveModelMixin):
serializer_class = ParserSerializer
filterset_class = ParserFilter
Expand Down
Loading

0 comments on commit 32c47c4

Please sign in to comment.