Skip to content

Commit

Permalink
Merge pull request #38 from intuitem/CA-150-Add-Security-measures-Req…
Browse files Browse the repository at this point in the history
…uirement-assessments-nested-tables-in-evidence

feat: add security measures and requirement assessments nested tables…
  • Loading branch information
eric-intuitem authored Feb 12, 2024
2 parents 525473f + 1a67954 commit 67d24f4
Show file tree
Hide file tree
Showing 27 changed files with 186 additions and 110 deletions.
7 changes: 5 additions & 2 deletions backend/app_tests/api/test_api_requirement_assessments.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def test_get_requirement_assessments(self, authenticated_client):
},
{
"folder": str(folder.id),
"compliance_assessment": str(compliance_assessment.id),
"compliance_assessment": {"id": str(compliance_assessment.id), "str": compliance_assessment.name},
"requirement": str(RequirementNode.objects.all()[0].id),
},
-1,
Expand Down Expand Up @@ -143,6 +143,9 @@ def test_create_requirement_assessments(self, authenticated_client):
"requirement": str(RequirementNode.objects.all()[0].id),
"security_measures": [str(security_measure.id)],
},
{
"compliance_assessment": {"id": str(compliance_assessment.id), "str": compliance_assessment.name}
},
base_count=-1,
)

Expand Down Expand Up @@ -186,7 +189,7 @@ def test_update_requirement_assessments(self, authenticated_client):
},
{
"folder": str(Folder.get_root_folder().id),
"compliance_assessment": str(compliance_assessment.id),
"compliance_assessment": {"id": str(compliance_assessment.id), "str": compliance_assessment.name},
"requirement": str(RequirementNode.objects.all()[0].id),
},
)
Expand Down
2 changes: 2 additions & 0 deletions backend/core/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@ class Meta:

class RequirementAssessmentReadSerializer(BaseModelSerializer):
name = serializers.CharField(source="__str__")
compliance_assessment = FieldsRelatedField()


class Meta:
model = RequirementAssessment
Expand Down
3 changes: 2 additions & 1 deletion backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ class SecurityMeasureViewSet(BaseModelViewSet):
"effort",
"risk_scenarios",
"requirement_assessments",
"evidences"
]
search_fields = ["name", "description", "risk_scenarios", "requirement_assessments"]

Expand Down Expand Up @@ -1161,7 +1162,7 @@ class RequirementAssessmentViewSet(BaseModelViewSet):
"""

model = RequirementAssessment
filterset_fields = ["folder"]
filterset_fields = ["folder", "evidences"]
search_fields = ["name", "description"]

@action(detail=False, name="Get updatable measures")
Expand Down
2 changes: 2 additions & 0 deletions frontend/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ node_modules
.env
.env.*
!.env.example
/tests/reports/*
/tests/results/*

# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
Expand Down
2 changes: 2 additions & 0 deletions frontend/.prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ node_modules
.env
.env.*
!.env.example
/tests/reports/*
/tests/results/*

# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
Expand Down
14 changes: 0 additions & 14 deletions frontend/src/lib/components/Forms/ModelForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -248,20 +248,6 @@
label="Domain"
hide={initialData.security_measures || initialData.requirement_assessments}
/>
<AutocompleteSelect
{form}
multiple
options={getOptions({ objects: model.foreignKeys['security_measures'] })}
field="security_measures"
label="Security measures"
/>
<AutocompleteSelect
{form}
multiple
options={getOptions({ objects: model.foreignKeys['requirement_assessments'] })}
field="requirement_assessments"
label="Requirement assessments"
/>
<TextField
{form}
field="link"
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lib/components/Modals/CreateModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
{$modalStore[0].title ?? '(title missing)'}
</header>
<div
role="button"
tabindex="0"
class="flex items-center hover:text-primary-500 cursor-pointer"
on:click={parent.onClose}
on:keydown={parent.onClose}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
$: display = LOCALE_DISPLAY_MAP[cell];
</script>

<span {...$$restProps}>{@html display}</span>
<span {...$$restProps}>{display}</span>
2 changes: 1 addition & 1 deletion frontend/src/lib/components/ModelTable/ModelTable.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
if (['Enter', 'Space'].includes(event.code)) onRowClick(event, rowIndex);
}
export let identifierField: string = 'id';
export let identifierField = 'id';
export let deleteForm: SuperValidated<AnyZodObject> | undefined = undefined;
Expand Down
17 changes: 7 additions & 10 deletions frontend/src/lib/components/SideBar/navData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,12 @@ export const navData = {
name: 'Assets',
fa_icon: 'fa-solid fa-gem',
href: '/assets'
},
}
]
},
{
name: 'Governance',
items: [

{
name: 'Policies',
fa_icon: 'fa-solid fa-user',
Expand All @@ -72,7 +71,7 @@ export const navData = {
name: 'Risk matrices',
fa_icon: 'fa-solid fa-table-cells-large',
href: '/risk-matrices'
},
}
]
},
{
Expand Down Expand Up @@ -116,10 +115,9 @@ export const navData = {
}
]
},
{
name: 'Organisation',
items : [

{
name: 'Organisation',
items: [
{
name: 'Domains',
fa_icon: 'fa-solid fa-diagram-project',
Expand All @@ -145,9 +143,8 @@ export const navData = {
fa_icon: 'fa-solid fa-user-tag',
href: '/role-assignments'
}
]

},
]
},

{
name: 'Extra',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
export let editURL: string | undefined;
export let deleteForm: SuperValidated<AnyZodObject> | undefined;
export let URLModel: urlModel | string | undefined;
export let identifierField: string = 'id';
export let identifierField = 'id';
export let hasBody = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,13 @@
}}
>
{#if typeof node.content === 'string'}
{@html node.content}
{node.content}
{:else}
<svelte:component this={node.content} {...node.contentProps} />
{/if}
<svelte:fragment slot="lead">
{#if typeof node.lead === 'string'}
{@html node.lead}
{node.lead}
{:else}
<svelte:component this={node.lead} {...node.leadProps} />
{/if}
Expand Down
16 changes: 8 additions & 8 deletions frontend/src/lib/components/fragments/WatchlistExceptions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,21 @@
request_path
)}'"
>
<td scope="row" class="px-3 py-4 font-medium">
<th scope="row" class="px-3 py-4 font-medium">
{#if acceptance.approver == user.id && acceptance.state == 'submitted'}
<span class="mr-1 p-1 rounded-md text-xs bg-indigo-500 text-white">
action requested
</span>
{/if}
{acceptance.name}
</td>
<td class="px-3 py-4">
</th>
<th class="px-3 py-4">
{acceptance.folder.str}
</td>
<td class="px-3 py-4">
</th>
<th class="px-3 py-4">
{acceptance.approver.str}
</td>
<td class="px-3 py-4">
</th>
<th class="px-3 py-4">
{#if acceptanceState(acceptance.expiry_date) === 'expired'}
<span class="rounded bg-red-500 text-white p-1 text-xs mr-1">expired</span>
{:else if acceptanceState(acceptance.expiry_date) === 'upcoming'}
Expand All @@ -66,7 +66,7 @@
<span class="rounded bg-yellow-500 text-white p-1 text-xs mr-1">today</span>
{/if}
{formatStringToDate(acceptance.expiry_date)}
</td>
</th>
</tr>
{/each}
{:else}
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lib/utils/crud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,8 @@ export const URL_MODEL_MAP: ModelMap = {
selectFields: [{ field: 'status' }],
foreignKeyFields: [
{ field: 'security_measures', urlModel: 'security-measures' },
{ field: 'evidences', urlModel: 'evidences' }
{ field: 'evidences', urlModel: 'evidences' },
{ field: 'compliance_assessment', urlModel: 'compliance-assessments' }
]
},
libraries: {
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/lib/utils/csrf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ async function getCsrfToken() {
credentials: 'include'
}).then((res) => res.json());
return response.csrfToken;
} catch (error) {}
} catch (error) {
console.error(error);
}
}

export const csrfToken = await getCsrfToken();
4 changes: 4 additions & 0 deletions frontend/src/lib/utils/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ export const listViewFields = {
head: ['Name', 'Framework', 'Description', 'Project'],
body: ['name', 'framework', 'description', 'project']
},
'requirement-assessments': {
head: ['Name', 'Description', 'Compliance Assessment'],
body: ['name', 'description', 'compliance_assessment']
},
evidences: {
head: ['Name', 'File', 'Description'],
body: ['name', 'attachment', 'description']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
if (node.children && Object.keys(node.children).length > 0) {
for (const childId in node.children) {
if (node.children.hasOwnProperty(childId)) {
if (Object.prototype.hasOwnProperty.call(node.children, childId)) {
const childNode = node.children[childId];
countStatus(childNode, statusCounts);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
export let security_functions: Record<string, any>[] | undefined = undefined;
export let children: Record<string, Record<string, unknown>> | undefined = undefined;
export let canEditRequirementAssessment: boolean;
export let status: string | undefined = undefined;
// export let status: string | undefined = undefined;
export let statusCounts: Record<string, number> | undefined;
$: hasChildren = children && Object.keys(children).length > 0;
Expand Down Expand Up @@ -87,7 +87,7 @@
{#if threats || security_functions}
<div
role="button"
tabindex="-1"
tabindex="0"
class="underline text-sm hover:text-primary-400 {classesShowInfoText(showInfo)}"
on:click={(_) => (showInfo = !showInfo)}
on:keydown={(_) => (showInfo = !showInfo)}
Expand Down Expand Up @@ -155,20 +155,20 @@
<div class="flex flex-1 bg-gray-200 rounded-full overflow-hidden h-4 shrink">
{#each orderedStatusPercentages as sp}
{#if complianceColorMap[sp.status] === '#000000'}
<div
class="flex flex-col justify-center overflow-hidden text-white text-xs text-center bg-yellow-500"
style="width: {sp.percentage.value}%; background-color: {complianceColorMap[sp.status]}"
>
{sp.percentage.display}%
</div>
<div
class="flex flex-col justify-center overflow-hidden text-white text-xs text-center bg-yellow-500"
style="width: {sp.percentage.value}%; background-color: {complianceColorMap[sp.status]}"
>
{sp.percentage.display}%
</div>
{:else}
<div
class="flex flex-col justify-center overflow-hidden text-black text-xs text-center bg-yellow-500"
style="width: {sp.percentage.value}%; background-color: {complianceColorMap[sp.status]}"
>
{sp.percentage.display}%
<!-- {sp.percentage?.display}% -->
</div>
<div
class="flex flex-col justify-center overflow-hidden text-black text-xs text-center bg-yellow-500"
style="width: {sp.percentage.value}%; background-color: {complianceColorMap[sp.status]}"
>
{sp.percentage.display}%
<!-- {sp.percentage?.display}% -->
</div>
{/if}
{/each}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
export let status: string;
// export let status: string;
export let statusDisplay: string;
export let statusColor: string;
export let assessable: boolean;
Expand Down
38 changes: 30 additions & 8 deletions frontend/src/routes/(app)/evidences/[id=uuid]/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import { z } from 'zod';
import { setError, superValidate } from 'sveltekit-superforms/server';
import { setFlash } from 'sveltekit-flash-message/server';
import type { PageServerLoad } from './$types';
import type { urlModel } from '$lib/utils/types';
import { listViewFields } from '$lib/utils/table';
import { tableSourceMapper, type TableSource } from '@skeletonlabs/skeleton';

export const load: PageServerLoad = (async ({ fetch, params }) => {
export const load: PageServerLoad = async ({ fetch, params }) => {
const URLModel = 'evidences';
const endpoint = `${BASE_API_URL}/${URLModel}/${params.id}/`;

Expand All @@ -14,8 +17,28 @@ export const load: PageServerLoad = (async ({ fetch, params }) => {

const object = await fetch(`${endpoint}object/`).then((res) => res.json());

return { URLModel, evidence, object };
})
const tables: Record<string, any> = {};

for (const key of ['security-measures', 'requirement-assessments'] as urlModel[]) {
const keyEndpoint = `${BASE_API_URL}/${key}/?evidences=${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}`);
}
}

return { URLModel, evidence, object, tables };
};

export const actions: Actions = {
deleteAttachment: async (event) => {
Expand All @@ -40,11 +63,10 @@ export const actions: Actions = {
if (response.non_field_errors) {
setError(deleteAttachmentForm, 'non_field_errors', response.non_field_errors);
}
setFlash({ type: "error", message: "An error has occured" }, event);
setFlash({ type: 'error', message: 'An error has occured' }, event);
return fail(400, { form: deleteAttachmentForm });
}
setFlash({ type: "success", message: "Attachment successfully deleted" }, event);
throw redirect(302, `/${urlmodel}/${id}`)
setFlash({ type: 'success', message: 'Attachment successfully deleted' }, event);
throw redirect(302, `/${urlmodel}/${id}`);
}

}
};
Loading

0 comments on commit 67d24f4

Please sign in to comment.