diff --git a/backend/app_tests/api/test_utils.py b/backend/app_tests/api/test_utils.py index 448c5073b..a973ebb6c 100644 --- a/backend/app_tests/api/test_utils.py +++ b/backend/app_tests/api/test_utils.py @@ -100,7 +100,7 @@ def expected_request_response( # User has access to the domain return False, expected_status, "ok" else: - return False, expected_status, "outside_scope" + return True, expected_status, "outside_scope" else: # User has not permission to perform the action if ( @@ -771,7 +771,7 @@ def update_object( ), f"{verbose_name} object detail can not be accessed with permission" else: assert ( - response.status_code == status.HTTP_403_FORBIDDEN + response.status_code == status.HTTP_404_NOT_FOUND ), f"{verbose_name} object detail can be accessed without permission" if not (fails or user_perm_fails): @@ -911,7 +911,7 @@ def delete_object( ), f"{verbose_name} object detail can not be accessed with permission" else: assert ( - response.status_code == status.HTTP_403_FORBIDDEN + response.status_code == status.HTTP_404_NOT_FOUND ), f"{verbose_name} object detail can be accessed without permission" # Asserts that the object was deleted successfully diff --git a/backend/core/helpers.py b/backend/core/helpers.py index 6745f371b..4b4afd492 100644 --- a/backend/core/helpers.py +++ b/backend/core/helpers.py @@ -1,25 +1,22 @@ +import json from collections.abc import MutableMapping from datetime import date, timedelta +from typing import Optional +from django.core.exceptions import NON_FIELD_ERRORS as DJ_NON_FIELD_ERRORS +from django.core.exceptions import ValidationError as DjValidationError from django.db.models import Count from django.shortcuts import get_object_or_404 -from iam.models import Folder, Permission, RoleAssignment, User +from rest_framework.exceptions import ValidationError as DRFValidationError +from rest_framework.views import api_settings +from rest_framework.views import exception_handler as drf_exception_handler +from iam.models import Folder, Permission, RoleAssignment, User from library.helpers import get_referential_translation from .models import * from .utils import camel_case -from typing import List, Dict, Optional - -import json - -from django.core.exceptions import NON_FIELD_ERRORS as DJ_NON_FIELD_ERRORS -from django.core.exceptions import ValidationError as DjValidationError -from rest_framework.exceptions import ValidationError as DRFValidationError -from rest_framework.views import api_settings -from rest_framework.views import exception_handler as drf_exception_handler - DRF_NON_FIELD_ERRORS = api_settings.NON_FIELD_ERRORS_KEY @@ -1142,6 +1139,30 @@ def threats_count_per_name(user: User): return {"labels": labels, "values": values} +def get_folder_content(folder: Folder): + content = [] + for f in Folder.objects.filter(parent_folder=folder).distinct(): + content.append({"name": f.name, "children": get_folder_content(f)}) + for p in Project.objects.filter(folder=folder).distinct(): + content.append( + { + "name": p.name, + "children": [ + { + "name": "audits", + "value": ComplianceAssessment.objects.filter(project=p).count(), + }, + { + "name": "risk assessments", + "value": RiskAssessment.objects.filter(project=p).count(), + }, + ], + } + ) + + return content + + def handle(exc, context): # translate django validation error which ... # .. causes HTTP 500 status ==> DRF validation which will cause 400 HTTP status diff --git a/backend/core/startup.py b/backend/core/startup.py index ec5079096..e87468a4c 100644 --- a/backend/core/startup.py +++ b/backend/core/startup.py @@ -11,28 +11,32 @@ logger = get_logger(__name__) READER_PERMISSIONS_LIST = [ - "view_project", - "view_riskassessment", "view_appliedcontrol", - "view_policy", - "view_riskscenario", - "view_riskacceptance", "view_asset", - "view_threat", - "view_referencecontrol", - "view_folder", - "view_usergroup", - "view_riskmatrix", "view_complianceassessment", - "view_requirementassessment", - "view_requirementnode", + "view_entity", + "view_entityassessment", "view_evidence", + "view_folder", "view_framework", "view_loadedlibrary", + "view_policy", + "view_project", + "view_referencecontrol", + "view_representative", + "view_requirementassessment", + "view_requirementmapping", + "view_requirementmappingset", + "view_requirementnode", + "view_riskacceptance", + "view_riskassessment", + "view_riskmatrix", + "view_riskscenario", + "view_solution", "view_storedlibrary", + "view_threat", "view_user", - "view_requirementmappingset", - "view_requirementmapping", + "view_usergroup", ] APPROVER_PERMISSIONS_LIST = [ @@ -62,120 +66,153 @@ ] ANALYST_PERMISSIONS_LIST = [ + "add_appliedcontrol", + "add_asset", + "add_complianceassessment", + "add_evidence", + "add_policy", "add_project", - "view_project", - "change_project", - "delete_project", + "add_riskacceptance", "add_riskassessment", - "view_riskassessment", - "change_riskassessment", - "delete_riskassessment", - "add_appliedcontrol", - "view_appliedcontrol", + "add_riskscenario", + "add_solution", + "add_threat", "change_appliedcontrol", - "delete_appliedcontrol", - "add_policy", - "view_policy", + "change_asset", + "change_complianceassessment", + "change_entity", + "change_entityassessment", + "change_evidence", "change_policy", - "delete_policy", - "add_riskscenario", - "view_riskscenario", - "change_riskscenario", - "delete_riskscenario", - "add_riskacceptance", - "view_riskacceptance", + "change_project", + "change_referencecontrol", + "change_representative", + "change_requirementassessment", "change_riskacceptance", - "delete_riskacceptance", - "add_complianceassessment", - "view_complianceassessment", - "change_complianceassessment", + "change_riskassessment", + "change_riskscenario", + "change_solution", + "change_threat", + "delete_appliedcontrol", + "delete_asset", "delete_complianceassessment", - "view_requirementassessment", - "change_requirementassessment", - "add_evidence", - "view_evidence", - "change_evidence", + "delete_entity", + "delete_entityassessment", "delete_evidence", - "add_asset", - "view_asset", - "change_asset", - "delete_asset", - "add_threat", - "view_threat", - "change_threat", + "delete_policy", + "delete_project", + "delete_referencecontrol", + "delete_representative", + "delete_riskacceptance", + "delete_riskassessment", + "delete_riskscenario", + "delete_solution", "delete_threat", - "view_referencecontrol", + "view_appliedcontrol", + "view_asset", + "view_complianceassessment", + "view_entity", + "view_entityassessment", + "view_evidence", "view_folder", - "view_usergroup", - "view_riskmatrix", - "view_requirementnode", "view_framework", - "view_storedlibrary", "view_loadedlibrary", - "view_user", - "view_requirementmappingset", + "view_policy", + "view_project", + "view_referencecontrol", + "view_representative", + "view_requirementassessment", "view_requirementmapping", + "view_requirementmappingset", + "view_requirementnode", + "view_riskacceptance", + "view_riskassessment", + "view_riskmatrix", + "view_riskscenario", + "view_solution", + "view_storedlibrary", + "view_threat", + "view_user", + "view_usergroup", ] DOMAIN_MANAGER_PERMISSIONS_LIST = [ - "change_usergroup", - "view_usergroup", - "add_project", - "change_project", - "delete_project", - "view_project", - "add_riskassessment", - "view_riskassessment", - "change_riskassessment", - "delete_riskassessment", "add_appliedcontrol", - "view_appliedcontrol", - "change_appliedcontrol", - "delete_appliedcontrol", + "add_asset", + "add_complianceassessment", + "add_entity", + "add_entityassessment", + "add_evidence", + "add_folder", "add_policy", - "view_policy", - "change_policy", - "delete_policy", - "add_riskscenario", - "view_riskscenario", - "change_riskscenario", - "delete_riskscenario", + "add_project", "add_riskacceptance", - "view_riskacceptance", - "change_riskacceptance", - "delete_riskacceptance", - "add_asset", - "view_asset", - "change_asset", - "delete_asset", + "add_riskassessment", + "add_riskmatrix", + "add_riskscenario", + "add_solution", "add_threat", - "view_threat", - "change_threat", - "delete_threat", - "view_referencecontrol", - "view_folder", + "change_appliedcontrol", + "change_asset", + "change_complianceassessment", + "change_entity", + "change_entityassessment", + "change_evidence", "change_folder", - "add_riskmatrix", - "view_riskmatrix", + "change_policy", + "change_project", + "change_referencecontrol", + "change_representative", + "change_requirementassessment", + "change_riskacceptance", + "change_riskassessment", "change_riskmatrix", + "change_riskscenario", + "change_solution", + "change_threat", + "delete_appliedcontrol", + "delete_asset", + "delete_complianceassessment", + "delete_entity", + "delete_entityassessment", + "delete_evidence", + "delete_folder", + "delete_policy", + "delete_project", + "delete_referencecontrol", + "delete_representative", + "delete_riskacceptance", + "delete_riskassessment", "delete_riskmatrix", - "add_complianceassessment", + "delete_riskscenario", + "delete_solution", + "delete_threat", + "view_appliedcontrol", + "view_asset", "view_complianceassessment", - "change_complianceassessment", - "delete_complianceassessment", - "view_requirementassessment", - "change_requirementassessment", - "add_evidence", + "view_entity", + "view_entityassessment", "view_evidence", - "change_evidence", - "delete_evidence", - "view_requirementnode", + "view_folder", "view_framework", - "view_storedlibrary", "view_loadedlibrary", - "view_user", - "view_requirementmappingset", + "view_policy", + "view_project", + "view_referencecontrol", + "view_representative", + "view_requirementassessment", "view_requirementmapping", + "view_requirementmappingset", + "view_requirementnode", + "view_riskacceptance", + "view_riskassessment", + "view_riskmatrix", + "view_riskscenario", + "view_solution", + "view_storedlibrary", + "view_threat", + "view_user", + "view_usergroup", ] ADMINISTRATOR_PERMISSIONS_LIST = [ diff --git a/backend/core/views.py b/backend/core/views.py index 892264fee..b5f901bee 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -1156,32 +1156,12 @@ def org_tree(self, request): object_type=Folder, ) folders_list = list() - for folder in Folder.objects.exclude(content_type="GL").filter( - id__in=viewable_objects + for folder in ( + Folder.objects.exclude(content_type="GL") + .filter(id__in=viewable_objects, parent_folder=Folder.get_root_folder()) + .distinct() ): - entry = {"name": folder.name} - children = [] - for project in Project.objects.filter(folder=folder): - children.append( - { - "name": project.name, - "children": [ - { - "name": "audits", - "value": ComplianceAssessment.objects.filter( - project=project - ).count(), - }, - { - "name": "risk assessments", - "value": RiskAssessment.objects.filter( - project=project - ).count(), - }, - ], - } - ) - entry.update({"children": children}) + entry = {"name": folder.name, "children": get_folder_content(folder)} folders_list.append(entry) tree.update({"children": folders_list}) diff --git a/backend/library/management/commands/storelibraries.py b/backend/library/management/commands/storelibraries.py index 4ac126240..617c74877 100644 --- a/backend/library/management/commands/storelibraries.py +++ b/backend/library/management/commands/storelibraries.py @@ -1,12 +1,14 @@ from pathlib import Path -import structlog +import structlog, signal from ciso_assistant.settings import LIBRARIES_PATH from core.models import StoredLibrary from django.core.management.base import BaseCommand logger = structlog.getLogger(__name__) +signal.signal(signal.SIGINT, signal.SIG_DFL) + class Command(BaseCommand): help = "Store libraries in the database" diff --git a/enterprise/frontend/src/lib/components/Forms/ModelForm.svelte b/enterprise/frontend/src/lib/components/Forms/ModelForm.svelte deleted file mode 100644 index 1ca3994eb..000000000 --- a/enterprise/frontend/src/lib/components/Forms/ModelForm.svelte +++ /dev/null @@ -1,1418 +0,0 @@ - - - createModalCache.deleteCache(model.urlModel)} - {...$$restProps} -> - - - - {#if shape.reference_control} - { - if (e.detail) { - await fetch(`/reference-controls/${e.detail}`) - .then((r) => r.json()) - .then((r) => { - form.form.update((currentData) => { - if ( - context === 'edit' && - currentData['reference_control'] === initialData['reference_control'] && - !updated_fields.has('reference_control') - ) { - return currentData; // Keep the current values in the edit form. - } - updated_fields.add('reference_control'); - return { ...currentData, category: r.category, csf_function: r.csf_function }; - }); - }); - } - }} - /> - {/if} - {#if shape.name} - - {/if} - {#if shape.description} - - {/if} - {#if URLModel === 'projects'} - - - - {:else if URLModel === 'folders'} - - {:else if URLModel === 'risk-assessments' || URLModel === 'risk-assessment-duplicate'} - - - {#if !riskAssessmentDuplication} - - - - - - - - {/if} - {:else if URLModel === 'threats'} - - - - - {:else if URLModel === 'risk-scenarios'} - - - {:else if URLModel === 'applied-controls' || URLModel === 'policies'} - {#if schema.shape.category} - - {/if} - - - - - - - - - - - {:else if URLModel === 'risk-acceptances'} - - {#if object.id && $page.data.user.id === object.approver} - - {/if} - - - - {:else if URLModel === 'reference-controls'} - - - - - - - {:else if URLModel === 'evidences'} - - - - {#if !(initialData.applied_controls || initialData.requirement_assessments)} - - {:else} - - {/if} - - {:else if URLModel === 'compliance-assessments'} - {#if context === 'fromBaseline' && initialData.baseline} - - {/if} - - - - { - if (e.detail) { - await fetch(`/frameworks/${e.detail}`) - .then((r) => r.json()) - .then((r) => { - const implementation_groups = r['implementation_groups_definition'] || []; - model.selectOptions['selected_implementation_groups'] = implementation_groups.map( - (group) => ({ label: group.name, value: group.ref_id }) - ); - }); - } - }} - /> - {#if model.selectOptions['selected_implementation_groups'] && model.selectOptions['selected_implementation_groups'].length} - - {/if} - - - - - - {:else if URLModel === 'assets'} - - - - - {:else if URLModel === 'requirement-assessments'} - - - - - - - {:else if URLModel === 'entities'} - - - - {:else if URLModel === 'entity-assessments'} - - {#if !data.compliance_assessment} - - { - if (e.detail) { - await fetch(`/frameworks/${e.detail}`) - .then((r) => r.json()) - .then((r) => { - const implementation_groups = r['implementation_groups_definition'] || []; - model.selectOptions['selected_implementation_groups'] = implementation_groups.map( - (group) => ({ label: group.name, value: group.ref_id }) - ); - }); - } - }} - /> - {#if model.selectOptions['selected_implementation_groups'] && model.selectOptions['selected_implementation_groups'].length} - - {/if} - {/if} - - - - - - - - - - - - - - - - {:else if URLModel === 'solutions'} - - - - {:else if URLModel === 'representatives'} - - {#if !data.user} - - {/if} - - - - - - {:else if URLModel === 'frameworks'} - - - {:else if URLModel === 'users'} - - {#if shape.first_name && shape.last_name} - - - {/if} - {#if shape.user_groups} - - {/if} - {#if shape.is_active} - - {/if} - {:else if URLModel === 'sso-settings'} - - - - {#if data.provider !== 'saml'} - - {m.IdPConfiguration()} - - - - - {#if data.provider !== 'saml'} - - - {/if} - - - {/if} - {#if data.provider === 'saml'} - - {m.SAMLIdPConfiguration()} - - - Option 1: Fill the metadata url - - - - {m.or()} - - - Option 2: Fill the SSO URL, SLO URL and x509cert - - - - - - - - {m.SPConfiguration()} - - - - - - {m.advancedSettings()} - - - - - - - - - - - - - - - - - - - - - - - - - {/if} - - {/if} - - {#if closeModal} - { - parent.onClose(event); - createModalCache.deleteCache(model.urlModel); - }}>{m.cancel()} - { - createModalCache.deleteCache(model.urlModel); - }}>{m.save()} - {:else} - {#if cancelButton} - {m.cancel()} - {/if} - {m.save()} - {/if} - - diff --git a/enterprise/frontend/src/lib/components/Forms/ModelForm/FolderForm.svelte b/enterprise/frontend/src/lib/components/Forms/ModelForm/FolderForm.svelte new file mode 100644 index 000000000..f9123d39a --- /dev/null +++ b/enterprise/frontend/src/lib/components/Forms/ModelForm/FolderForm.svelte @@ -0,0 +1,23 @@ + + + diff --git a/frontend/messages/ar.json b/frontend/messages/ar.json index e7c14d304..8ff33880a 100644 --- a/frontend/messages/ar.json +++ b/frontend/messages/ar.json @@ -145,6 +145,7 @@ "justification": "التبرير", "parentFolder": "المجلد الرئيسي", "contentType": "نوع المحتوى", + "objectType": "نوع الكائن", "type": "النوع", "lcStatus": "حالة LC", "internalReference": "المرجع الداخلي", diff --git a/frontend/messages/de.json b/frontend/messages/de.json index 16dba3009..ba2b3b7d3 100644 --- a/frontend/messages/de.json +++ b/frontend/messages/de.json @@ -156,6 +156,7 @@ "justification": "Begründung", "parentFolder": "Übergeordneter Ordner", "contentType": "Inhaltstyp", + "objectType": "Objekttyp", "type": "Typ", "lcStatus": "Status", "internalReference": "Interne Referenz", diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 8a047525b..1558fdb8a 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -157,6 +157,7 @@ "justification": "Justification", "parentFolder": "Parent folder", "contentType": "Content type", + "objectType": "Object type", "type": "Type", "lcStatus": "Status", "internalReference": "Internal reference", diff --git a/frontend/messages/es.json b/frontend/messages/es.json index df0cc23e1..3ab1f6881 100644 --- a/frontend/messages/es.json +++ b/frontend/messages/es.json @@ -156,6 +156,7 @@ "justification": "Justificación", "parentFolder": "Carpeta principal", "contentType": "Tipo de contenido", + "objectType": "abcdef", "type": "Tipo", "lcStatus": "Estado", "internalReference": "Referencia interna", diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json index 64c376308..5155a3894 100644 --- a/frontend/messages/fr.json +++ b/frontend/messages/fr.json @@ -156,6 +156,7 @@ "justification": "Justification", "parentFolder": "Domaine parent", "contentType": "Type de contenu", + "objectType": "Type d'objet", "type": "Type", "lcStatus": "Statut", "internalReference": "Référence interne", diff --git a/frontend/messages/hi.json b/frontend/messages/hi.json index 9effdf8e8..f1db6f897 100644 --- a/frontend/messages/hi.json +++ b/frontend/messages/hi.json @@ -154,6 +154,7 @@ "justification": "औचित्य", "parentFolder": "अभिभावक फ़ोल्डर", "contentType": "सामग्री प्रकार", + "objectType": "ऑब्जेक्ट प्रकार", "type": "प्रकार", "lcStatus": "स्थिति", "internalReference": "आंतरिक संदर्भ", diff --git a/frontend/messages/it.json b/frontend/messages/it.json index 1f5981730..c425d66ea 100644 --- a/frontend/messages/it.json +++ b/frontend/messages/it.json @@ -156,6 +156,7 @@ "justification": "Giustificazione", "parentFolder": "Cartella principale", "contentType": "Tipo di contenuto", + "objectType": "Tipo di oggetto", "type": "Tipo", "lcStatus": "Stato", "internalReference": "Riferimento interno", diff --git a/frontend/messages/nl.json b/frontend/messages/nl.json index 73a343c28..462af7c6b 100644 --- a/frontend/messages/nl.json +++ b/frontend/messages/nl.json @@ -156,6 +156,7 @@ "justification": "Rechtvaardiging", "parentFolder": "Bovenliggende map", "contentType": "Inhoudstype", + "objectType": "Objecttype", "type": "Type", "lcStatus": "Status", "internalReference": "Interne referentie", diff --git a/frontend/messages/pl.json b/frontend/messages/pl.json index 7047be37e..d1112f001 100644 --- a/frontend/messages/pl.json +++ b/frontend/messages/pl.json @@ -156,6 +156,7 @@ "justification": "Uzasadnienie", "parentFolder": "Folder nadrzędny", "contentType": "Rodzaj treści", + "objectType": "Typ obiektu", "type": "Typ", "lcStatus": "Status LC", "internalReference": "Referencja wewnętrzna", diff --git a/frontend/messages/pt.json b/frontend/messages/pt.json index 9f4836ab8..e72d39de5 100644 --- a/frontend/messages/pt.json +++ b/frontend/messages/pt.json @@ -156,6 +156,7 @@ "justification": "Justificativa", "parentFolder": "Pasta pai", "contentType": "Tipo de conteúdo", + "objectType": "Tipo de objeto", "type": "Tipo", "lcStatus": "Status", "internalReference": "Referência interna", diff --git a/frontend/messages/ro.json b/frontend/messages/ro.json index 4a5226b39..1a1522311 100644 --- a/frontend/messages/ro.json +++ b/frontend/messages/ro.json @@ -152,6 +152,7 @@ "justification": "Justificare", "parentFolder": "Dosar părinte", "contentType": "Tip conținut", + "objectType": "Tipul obiectului", "type": "Tip", "lcStatus": "Stare LC", "internalReference": "Referință internă", diff --git a/frontend/messages/ur.json b/frontend/messages/ur.json index a3020efaf..973b9d377 100644 --- a/frontend/messages/ur.json +++ b/frontend/messages/ur.json @@ -154,6 +154,7 @@ "justification": "جواز", "parentFolder": "مرکزی فولڈر", "contentType": "مواد کی قسم", + "objectType": "آبجیکٹ کی قسم", "type": "قسم", "lcStatus": "حیثیت", "internalReference": "داخلی حوالہ", diff --git a/frontend/src/lib/components/Chart/TreeChart.svelte b/frontend/src/lib/components/Chart/TreeChart.svelte index d94357e52..957da77f3 100644 --- a/frontend/src/lib/components/Chart/TreeChart.svelte +++ b/frontend/src/lib/components/Chart/TreeChart.svelte @@ -1,6 +1,5 @@ diff --git a/frontend/src/lib/components/Forms/Score.svelte b/frontend/src/lib/components/Forms/Score.svelte index d094e05b4..2a85457a8 100644 --- a/frontend/src/lib/components/Forms/Score.svelte +++ b/frontend/src/lib/components/Forms/Score.svelte @@ -26,8 +26,6 @@ export let form: SuperForm>; const { value, errors, constraints } = formFieldProxy(form, field); - $value = $value ?? min_score; - let isScored = formFieldProxy(form, 'is_scored')['value']; if (always_enabled) { diff --git a/frontend/src/lib/utils/crud.ts b/frontend/src/lib/utils/crud.ts index 99da36886..62bd8ad52 100644 --- a/frontend/src/lib/utils/crud.ts +++ b/frontend/src/lib/utils/crud.ts @@ -17,7 +17,7 @@ type GetOptionsParams = { selfSelect?: boolean; }; -export function checkConstraints(constraints: { [key: string]: any }, foreignKeys: any) { +export function checkConstraints(constraints: { [key: string]: any }, foreignKeys: any): string[] { const emptyConstraintsList = []; for (const [key, constraint] of Object.entries(constraints)) { if (constraint.required && foreignKeys[key]) diff --git a/frontend/src/lib/utils/helpers.ts b/frontend/src/lib/utils/helpers.ts index 367b679f1..e40ce9e98 100644 --- a/frontend/src/lib/utils/helpers.ts +++ b/frontend/src/lib/utils/helpers.ts @@ -60,7 +60,7 @@ export function formatScoreValue(value: number, max_score: number, fullDonut = f } export function getSecureRedirect(url: any): string { - const SECURE_REDIRECT_URL_REGEX = /^\/(?!.*\/\/)[^\s]*$/; + const SECURE_REDIRECT_URL_REGEX = /^\/[^/]/; return typeof url === 'string' && SECURE_REDIRECT_URL_REGEX.test(url) ? url : ''; } diff --git a/frontend/src/lib/utils/table.ts b/frontend/src/lib/utils/table.ts index e3bc7eb21..2e5994eb2 100644 --- a/frontend/src/lib/utils/table.ts +++ b/frontend/src/lib/utils/table.ts @@ -227,6 +227,31 @@ const OWNER_FILTER: ListViewFilterConfig = { } }; */ +const LIBRARY_TYPE_FILTER = { + component: SelectFilter, + getColumn: (row) => { + const overviewKeys = new Set(row.overview.map((overviewRow) => overviewRow.split(':')[0])); + const libraryDatatypeSet = new Set([ + 'framework', + 'risk_matrix', + 'threats', + 'requirement_mapping_set', + 'reference_controls' + ]); + const datatypes = [...libraryDatatypeSet].filter((datatype) => overviewKeys.has(datatype)); + return datatypes; + }, + extraProps: { + defaultOptionName: 'objectType', + optionLabels: { + reference_controls: 'referenceControls', + requirement_mapping_set: 'requirementMappingSet', + risk_matrix: 'riskMatrix' + } + }, + alwaysDisplay: true +}; + export const listViewFields: ListViewFieldsConfig = { folders: { head: ['name', 'description', 'parentDomain'], @@ -409,8 +434,8 @@ export const listViewFields: ListViewFieldsConfig = { body: ['provider', 'name', 'description', 'locales', 'overview'], filters: { locales: LANGUAGE_FILTER, - provider: PROVIDER_FILTER_FOR_LIBRARIES - // has_risk_matrix: HAS_RISK_MATRIX_FILTER + provider: PROVIDER_FILTER_FOR_LIBRARIES, + objectType: LIBRARY_TYPE_FILTER } }, 'loaded-libraries': { @@ -418,7 +443,8 @@ export const listViewFields: ListViewFieldsConfig = { body: ['provider', 'name', 'description', 'locales', 'overview'], filters: { locales: LANGUAGE_FILTER, - provider: PROVIDER_FILTER_FOR_LIBRARIES + provider: PROVIDER_FILTER_FOR_LIBRARIES, + objectType: LIBRARY_TYPE_FILTER } }, 'sso-settings': { diff --git a/frontend/src/routes/(app)/(third-party)/evidences/[id=uuid]/attachment/+server.ts b/frontend/src/routes/(app)/(third-party)/evidences/[id=uuid]/attachment/+server.ts index ed87e123c..9ac07bd10 100644 --- a/frontend/src/routes/(app)/(third-party)/evidences/[id=uuid]/attachment/+server.ts +++ b/frontend/src/routes/(app)/(third-party)/evidences/[id=uuid]/attachment/+server.ts @@ -29,9 +29,13 @@ export const GET: RequestHandler = async ({ fetch, setHeaders, params }) => { } const reader = attachmentResponse.body.getReader(); + let readerTerminated = false; const stream = new ReadableStream({ start(controller) { function push() { + if (readerTerminated) { + return; + } reader.read().then(({ done, value }) => { if (done) { controller.close(); @@ -42,6 +46,9 @@ export const GET: RequestHandler = async ({ fetch, setHeaders, params }) => { }); } push(); + }, + cancel() { + readerTerminated = true; } }); diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts index 965b61acb..255d8ae8e 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts @@ -49,6 +49,7 @@ export const load = (async ({ fetch, params }) => { }); const schema = modelSchema(URLModel); + object.evidences = object.evidences.map((evidence) => evidence.id); const form = await superValidate(object, zod(schema), { errors: true }); const foreignKeys: Record = {}; @@ -102,12 +103,40 @@ export const load = (async ({ fetch, params }) => { measureSelectOptions[selectField.field] = Object.entries(data).map(([key, value]) => ({ label: value, value: key - })); + })) + + } else { + console.error(`Failed to fetch data for ${selectField.field}: ${response.statusText}`); } }) - ); + ) } - measureModel.selectOptions = measureSelectOptions; + + measureModel['selectOptions'] = measureSelectOptions; + + const tables: Record = {}; + + await Promise.all( + ['applied-controls', 'evidences'].map(async (key) => { + const keyEndpoint = `${BASE_API_URL}/${key}/?requirement_assessments=${params.id}`; + const response = await fetch(keyEndpoint); + + if (response.ok) { + const data = await response.json().then((data) => data.results); + + const bodyData = tableSourceMapper(data, listViewFields[key].body); + + const table: TableSource = { + head: listViewFields[key].head, + body: bodyData, + meta: data + }; + tables[key] = table; + } else { + console.error(`Failed to fetch data for ${key}: ${response.statusText}`); + } + }) + ); const measureForeignKeys: Record = {}; if (measureModel.foreignKeyFields) { @@ -128,20 +157,6 @@ export const load = (async ({ fetch, params }) => { } measureModel.foreignKeys = measureForeignKeys; - const tables: Record = {}; - await Promise.all( - ['applied-controls', 'evidences'].map(async (key) => { - const data = await fetchJson(`${baseUrl}/${key}/?requirement_assessments=${params.id}`); - if (data) { - tables[key] = { - head: listViewFields[key].head, - body: tableSourceMapper(data.results, listViewFields[key].body), - meta: data.results - }; - } - }) - ); - const evidenceModel = getModelInfo('evidences'); const evidenceCreateSchema = modelSchema('evidences'); const evidenceCreateForm = await superValidate( diff --git a/frontend/tests/utils/page-content.ts b/frontend/tests/utils/page-content.ts index b425aeced..90a34a935 100644 --- a/frontend/tests/utils/page-content.ts +++ b/frontend/tests/utils/page-content.ts @@ -66,7 +66,7 @@ export class PageContent extends BasePage { } async importLibrary(name: string, urn?: string, language = 'English') { - this.page.getByTestId('search-input').fill(name); + await this.page.getByTestId('search-input').fill(name); if ( (await this.tab('Loaded libraries').isVisible()) && (await this.tab('Loaded libraries').getAttribute('aria-selected')) === 'true' @@ -80,10 +80,12 @@ export class PageContent extends BasePage { } // If the library is not visible, it might have already been loaded if (await this.importItemButton(name, language === 'any' ? undefined : language).isHidden()) { - await this.tab('Loaded libraries').click(); - expect(this.tab('Loaded libraries').getAttribute('aria-selected')).toBeTruthy(); - this.page.getByTestId('search-input').fill(name); - expect(this.getRow(name)).toBeVisible(); + if (await this.tab('Loaded libraries').isVisible()) { + await this.tab('Loaded libraries').click(); + expect(this.tab('Loaded libraries').getAttribute('aria-selected')).toBeTruthy(); + await this.page.getByTestId('search-input').fill(name); + } + await expect(this.getRow(name)).toBeVisible(); return; } await this.importItemButton(name, language === 'any' ? undefined : language).click(); @@ -92,7 +94,8 @@ export class PageContent extends BasePage { }); await this.tab('Loaded libraries').click(); expect(this.tab('Loaded libraries').getAttribute('aria-selected')).toBeTruthy(); - expect(this.getRow(name)).toBeVisible(); + await this.page.getByTestId('search-input').fill(name); + await expect(this.getRow(name)).toBeVisible(); } async viewItemDetail(value?: string) { diff --git a/frontend/tests/utils/test-data.ts b/frontend/tests/utils/test-data.ts index 2d5725daf..3df5269f7 100644 --- a/frontend/tests/utils/test-data.ts +++ b/frontend/tests/utils/test-data.ts @@ -26,140 +26,186 @@ export default { analyst: { name: 'Analyst', perms: [ + 'add_appliedcontrol', + 'add_asset', + 'add_complianceassessment', + 'add_evidence', + 'add_policy', 'add_project', - 'view_project', - 'change_project', - 'delete_project', + 'add_riskacceptance', 'add_riskassessment', - 'view_riskassessment', - 'change_riskassessment', - 'delete_riskassessment', - 'add_appliedcontrol', - 'view_appliedcontrol', + 'add_riskscenario', + 'add_solution', + 'add_threat', 'change_appliedcontrol', - 'delete_appliedcontrol', - 'add_policy', - 'view_policy', + 'change_asset', + 'change_complianceassessment', + 'change_entity', + 'change_entityassessment', + 'change_evidence', 'change_policy', - 'delete_policy', - 'add_riskscenario', - 'view_riskscenario', - 'change_riskscenario', - 'delete_riskscenario', - 'add_riskacceptance', - 'view_riskacceptance', + 'change_project', + 'change_referencecontrol', + 'change_representative', + 'change_requirementassessment', 'change_riskacceptance', - 'delete_riskacceptance', - 'add_complianceassessment', - 'view_complianceassessment', - 'change_complianceassessment', + 'change_riskassessment', + 'change_riskscenario', + 'change_solution', + 'change_threat', + 'delete_appliedcontrol', + 'delete_asset', 'delete_complianceassessment', - 'view_requirementassessment', - 'change_requirementassessment', - 'add_evidence', - 'view_evidence', - 'change_evidence', + 'delete_entity', + 'delete_entityassessment', 'delete_evidence', - 'add_asset', - 'view_asset', - 'change_asset', - 'delete_asset', - 'add_threat', - 'view_threat', - 'change_threat', + 'delete_policy', + 'delete_project', + 'delete_referencecontrol', + 'delete_representative', + 'delete_riskacceptance', + 'delete_riskassessment', + 'delete_riskscenario', + 'delete_solution', 'delete_threat', - 'view_referencecontrol', + 'view_appliedcontrol', + 'view_asset', + 'view_complianceassessment', + 'view_entity', + 'view_entityassessment', + 'view_evidence', 'view_folder', - 'view_usergroup', - 'view_riskmatrix', - 'view_requirementnode', 'view_framework', 'view_loadedlibrary', - 'view_user' + 'view_policy', + 'view_project', + 'view_referencecontrol', + 'view_representative', + 'view_requirementassessment', + 'view_requirementmapping', + 'view_requirementmappingset', + 'view_requirementnode', + 'view_riskacceptance', + 'view_riskassessment', + 'view_riskmatrix', + 'view_riskscenario', + 'view_solution', + 'view_storedlibrary', + 'view_threat', + 'view_user', + 'view_usergroup' ] }, reader: { name: 'Reader', perms: [ - 'view_project', - 'view_riskassessment', 'view_appliedcontrol', - 'view_policy', - 'view_riskscenario', - 'view_riskacceptance', 'view_asset', - 'view_threat', - 'view_referencecontrol', - 'view_folder', - 'view_usergroup', - 'view_riskmatrix', 'view_complianceassessment', - 'view_requirementassessment', - 'view_requirementnode', + 'view_entity', + 'view_entityassessment', 'view_evidence', + 'view_folder', 'view_framework', 'view_loadedlibrary', - 'view_user' + 'view_policy', + 'view_project', + 'view_referencecontrol', + 'view_representative', + 'view_requirementassessment', + 'view_requirementmapping', + 'view_requirementmappingset', + 'view_requirementnode', + 'view_riskacceptance', + 'view_riskassessment', + 'view_riskmatrix', + 'view_riskscenario', + 'view_solution', + 'view_storedlibrary', + 'view_threat', + 'view_user', + 'view_usergroup' ] }, domainManager: { name: 'Domain manager', perms: [ - 'change_usergroup', - 'view_usergroup', - 'add_project', - 'change_project', - 'delete_project', - 'view_project', - 'add_riskassessment', - 'view_riskassessment', - 'change_riskassessment', - 'delete_riskassessment', 'add_appliedcontrol', - 'view_appliedcontrol', - 'change_appliedcontrol', - 'delete_appliedcontrol', + 'add_asset', + 'add_complianceassessment', + 'add_entity', + 'add_entityassessment', + 'add_evidence', + 'add_folder', 'add_policy', - 'view_policy', - 'change_policy', - 'delete_policy', - 'add_riskscenario', - 'view_riskscenario', - 'change_riskscenario', - 'delete_riskscenario', + 'add_project', 'add_riskacceptance', - 'view_riskacceptance', - 'change_riskacceptance', - 'delete_riskacceptance', - 'add_asset', - 'view_asset', - 'change_asset', - 'delete_asset', + 'add_riskassessment', + 'add_riskmatrix', + 'add_riskscenario', + 'add_solution', 'add_threat', - 'view_threat', - 'change_threat', - 'delete_threat', - 'view_referencecontrol', - 'view_folder', + 'change_appliedcontrol', + 'change_asset', + 'change_complianceassessment', + 'change_entity', + 'change_entityassessment', + 'change_evidence', 'change_folder', - 'add_riskmatrix', - 'view_riskmatrix', + 'change_policy', + 'change_project', + 'change_referencecontrol', + 'change_representative', + 'change_requirementassessment', + 'change_riskacceptance', + 'change_riskassessment', 'change_riskmatrix', + 'change_riskscenario', + 'change_solution', + 'change_threat', + 'delete_appliedcontrol', + 'delete_asset', + 'delete_complianceassessment', + 'delete_entity', + 'delete_entityassessment', + 'delete_evidence', + 'delete_folder', + 'delete_policy', + 'delete_project', + 'delete_referencecontrol', + 'delete_representative', + 'delete_riskacceptance', + 'delete_riskassessment', 'delete_riskmatrix', - 'add_complianceassessment', + 'delete_riskscenario', + 'delete_solution', + 'delete_threat', + 'view_appliedcontrol', + 'view_asset', 'view_complianceassessment', - 'change_complianceassessment', - 'delete_complianceassessment', - 'view_requirementassessment', - 'change_requirementassessment', - 'add_evidence', + 'view_entity', + 'view_entityassessment', 'view_evidence', - 'change_evidence', - 'delete_evidence', - 'view_requirementnode', + 'view_folder', 'view_framework', 'view_loadedlibrary', - 'view_user' + 'view_policy', + 'view_project', + 'view_referencecontrol', + 'view_representative', + 'view_requirementassessment', + 'view_requirementmapping', + 'view_requirementmappingset', + 'view_requirementnode', + 'view_riskacceptance', + 'view_riskassessment', + 'view_riskmatrix', + 'view_riskscenario', + 'view_solution', + 'view_storedlibrary', + 'view_threat', + 'view_user', + 'view_usergroup' ] }, approver: { @@ -183,8 +229,11 @@ export default { 'view_requirementnode', 'view_evidence', 'view_framework', + 'view_storedlibrary', 'view_loadedlibrary', - 'view_user' + 'view_user', + 'view_requirementmappingset', + 'view_requirementmapping' ] } },
Option 1: Fill the metadata url
Option 2: Fill the SSO URL, SLO URL and x509cert