Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add security measures and requirement assessments nested tables… #38

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -20,8 +20,8 @@
export let form: SuperValidated<AnyZodObject>;
export let model: ModelInfo;
export let closeModal = false;
export let parent: any;

Check warning on line 23 in frontend/src/lib/components/Forms/ModelForm.svelte

View workflow job for this annotation

GitHub Actions / build (20)

Unexpected any. Specify a different type
export let suggestions: { [key: string]: any } = {};

Check warning on line 24 in frontend/src/lib/components/Forms/ModelForm.svelte

View workflow job for this annotation

GitHub Actions / build (20)

Unexpected any. Specify a different type

const URLModel = model.urlModel as urlModel;
export let schema = modelSchema(URLModel);
Expand Down 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
Loading