feat: As a user I want to delete my finding
This commit is contained in:
parent
27a8e963e9
commit
6a625349c8
|
@ -20,6 +20,8 @@ import {Category} from '@shared/models/category.model';
|
||||||
import {PentestStatus} from '@shared/models/pentest-status.model';
|
import {PentestStatus} from '@shared/models/pentest-status.model';
|
||||||
import {FindingDialogService} from '@shared/modules/finding-dialog/service/finding-dialog.service';
|
import {FindingDialogService} from '@shared/modules/finding-dialog/service/finding-dialog.service';
|
||||||
import {FindingDialogServiceMock} from '@shared/modules/finding-dialog/service/finding-dialog.service.mock';
|
import {FindingDialogServiceMock} from '@shared/modules/finding-dialog/service/finding-dialog.service.mock';
|
||||||
|
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||||
|
import {DialogServiceMock} from '@shared/services/dialog-service/dialog.service.mock';
|
||||||
|
|
||||||
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
const DESIRED_PROJECT_STATE_SESSION: ProjectStateModel = {
|
||||||
selectedProject: {
|
selectedProject: {
|
||||||
|
@ -77,6 +79,7 @@ describe('PentestFindingsComponent', () => {
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: NotificationService, useValue: new NotificationServiceMock()},
|
{provide: NotificationService, useValue: new NotificationServiceMock()},
|
||||||
|
{provide: DialogService, useClass: DialogServiceMock},
|
||||||
{provide: FindingDialogService, useClass: FindingDialogServiceMock},
|
{provide: FindingDialogService, useClass: FindingDialogServiceMock},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {PentestService} from '@shared/services/pentest.service';
|
||||||
import {BehaviorSubject, Observable} from 'rxjs';
|
import {BehaviorSubject, Observable} from 'rxjs';
|
||||||
import {Pentest} from '@shared/models/pentest.model';
|
import {Pentest} from '@shared/models/pentest.model';
|
||||||
import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy';
|
import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy';
|
||||||
import {filter, mergeMap, tap} from 'rxjs/operators';
|
import {catchError, filter, mergeMap, switchMap, tap} from 'rxjs/operators';
|
||||||
import {NotificationService, PopupType} from '@shared/services/notification.service';
|
import {NotificationService, PopupType} from '@shared/services/notification.service';
|
||||||
import {
|
import {
|
||||||
Finding,
|
Finding,
|
||||||
|
@ -21,6 +21,7 @@ import {PentestStatus} from '@shared/models/pentest-status.model';
|
||||||
import {Store} from '@ngxs/store';
|
import {Store} from '@ngxs/store';
|
||||||
import {UpdatePentestFindings} from '@shared/stores/project-state/project-state.actions';
|
import {UpdatePentestFindings} from '@shared/stores/project-state/project-state.actions';
|
||||||
import {ProjectState} from '@shared/stores/project-state/project-state';
|
import {ProjectState} from '@shared/stores/project-state/project-state';
|
||||||
|
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||||
|
|
||||||
@UntilDestroy()
|
@UntilDestroy()
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -33,6 +34,7 @@ export class PentestFindingsComponent implements OnInit {
|
||||||
constructor(private readonly pentestService: PentestService,
|
constructor(private readonly pentestService: PentestService,
|
||||||
private dataSourceBuilder: NbTreeGridDataSourceBuilder<FindingEntry>,
|
private dataSourceBuilder: NbTreeGridDataSourceBuilder<FindingEntry>,
|
||||||
private notificationService: NotificationService,
|
private notificationService: NotificationService,
|
||||||
|
private dialogService: DialogService,
|
||||||
private findingDialogService: FindingDialogService,
|
private findingDialogService: FindingDialogService,
|
||||||
private store: Store) {
|
private store: Store) {
|
||||||
this.dataSource = dataSourceBuilder.create(this.data, this.getters);
|
this.dataSource = dataSourceBuilder.create(this.data, this.getters);
|
||||||
|
@ -180,7 +182,32 @@ export class PentestFindingsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickDeleteFinding(findingEntry): void {
|
onClickDeleteFinding(findingEntry): void {
|
||||||
console.info('Coming soon..', findingEntry.data.findingId);
|
const message = {
|
||||||
|
title: 'finding.delete.title',
|
||||||
|
key: 'finding.delete.key',
|
||||||
|
data: {name: findingEntry.data.title},
|
||||||
|
};
|
||||||
|
this.dialogService.openConfirmDialog(
|
||||||
|
message
|
||||||
|
).onClose.pipe(
|
||||||
|
filter((confirm) => !!confirm),
|
||||||
|
switchMap(() => this.pentestService.deleteFindingByPentestAndFindingId(
|
||||||
|
this.pentestInfo$.getValue() ? this.pentestInfo$.getValue().id : '',
|
||||||
|
findingEntry.data.findingId)
|
||||||
|
),
|
||||||
|
catchError(() => {
|
||||||
|
this.notificationService.showPopup('finding.popup.delete.failed', PopupType.FAILURE);
|
||||||
|
return [];
|
||||||
|
}),
|
||||||
|
untilDestroyed(this)
|
||||||
|
).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.loadFindingsData();
|
||||||
|
this.notificationService.showPopup('finding.popup.delete.success', PopupType.SUCCESS);
|
||||||
|
}, error: error => {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isLoading(): Observable<boolean> {
|
isLoading(): Observable<boolean> {
|
||||||
|
|
|
@ -113,6 +113,15 @@ export class PentestService {
|
||||||
return this.http.patch<Finding>(`${this.apiBaseURL}/${findingId}/finding`, finding);
|
return this.http.patch<Finding>(`${this.apiBaseURL}/${findingId}/finding`, finding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete Finding
|
||||||
|
* @param pentestId the id of the pentest
|
||||||
|
* @param findingId the id of the finding
|
||||||
|
*/
|
||||||
|
public deleteFindingByPentestAndFindingId(pentestId: string, findingId: string): Observable<string> {
|
||||||
|
return this.http.delete<string>(`${this.apiBaseURL}/${pentestId}/finding/${findingId}`);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Comments for Pentest Id
|
* Get Comments for Pentest Id
|
||||||
* @param pentestId the id of the project
|
* @param pentestId the id of the project
|
||||||
|
|
|
@ -418,6 +418,42 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"response": []
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "deleteFinding",
|
||||||
|
"request": {
|
||||||
|
"auth": {
|
||||||
|
"type": "bearer",
|
||||||
|
"bearer": [
|
||||||
|
{
|
||||||
|
"key": "token",
|
||||||
|
"value": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItdG1lbEV0ZHhGTnRSMW9aNXlRdE5jaFFpX0RVN2VNeV9YcU44aXY0S3hzIn0.eyJleHAiOjE2NzA0MTQ3ODYsImlhdCI6MTY3MDQxNDQ4NiwianRpIjoiM2FmOWU5M2MtY2YzNi00MjQwLTkzNWEtNDkxYTJkZTY2MWU4IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4ODg4L2F1dGgvcmVhbG1zL2M0cG9fcmVhbG1fbG9jYWwiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMTBlMDZkN2EtOGRkMC00ZWNkLTg5NjMtMDU2YjQ1MDc5YzRmIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYzRwb19sb2NhbCIsInNlc3Npb25fc3RhdGUiOiI5M2ExNTBlMC03ZWRkLTQxZTgtYWE4Yi0yZWY5YTgzOWU4NDciLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbIioiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImM0cG9fdXNlciIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJjNHBvX2xvY2FsIjp7InJvbGVzIjpbInVzZXIiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6InRlc3QgdXNlciIsInByZWZlcnJlZF91c2VybmFtZSI6InR0dCIsImdpdmVuX25hbWUiOiJ0ZXN0IiwiZmFtaWx5X25hbWUiOiJ1c2VyIn0.QjUkCInyCJ5Wsz4q56gfsLqERr6pYlGjwNw-VsKNJ_3Jp-8Dazq9UmDGN8AmAkQ0sp0b-FMm3jArKMBpr84gKd65trvQx_qHvXev5x2MWBG4_9v3C9MmjxWcAYRVmfRdURUOhfto-4YfRwMwNRsKJfwMIjfS5VT8bHJWipcCDzaidN8h_LLORbmmQZ2o0l4Jnv5qrrWzUcSTeEeBpHGOjes1-T0gOlDJa34Z9x_xrsTsybKAylrmX03mDSI-f2h5XqqtgnrxtddtHXHatfxB1BHWq-FILDsGf0UG47FEQjqapFvn9bFiNyq0GVrgdK42miEO7ywOtCOKpCfAUnMwdQ",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "undefined",
|
||||||
|
"type": "any"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"method": "DELETE",
|
||||||
|
"header": [],
|
||||||
|
"url": {
|
||||||
|
"raw": "http://localhost:8443/pentests/11601f51-bc17-47fd-847d-0c53df5405b5/finding/cb33fad4-7965-4654-a9f9-f007edaca35c",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"localhost"
|
||||||
|
],
|
||||||
|
"port": "8443",
|
||||||
|
"path": [
|
||||||
|
"pentests",
|
||||||
|
"11601f51-bc17-47fd-847d-0c53df5405b5",
|
||||||
|
"finding",
|
||||||
|
"cb33fad4-7965-4654-a9f9-f007edaca35c"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -235,11 +235,33 @@ include::{snippets}/updateFindingById/http-response.adoc[]
|
||||||
|
|
||||||
include::{snippets}/updateFindingById/response-fields.adoc[]
|
include::{snippets}/updateFindingById/response-fields.adoc[]
|
||||||
|
|
||||||
|
=== Delete finding
|
||||||
|
|
||||||
|
To delete a finding, call the PATCH request /pentests/+{pentestId}+/finding/+{findingId}+
|
||||||
|
|
||||||
|
==== Request example
|
||||||
|
|
||||||
|
include::{snippets}/deleteFindingByPentestAndFindingId/http-request.adoc[]
|
||||||
|
|
||||||
|
==== Request structure
|
||||||
|
|
||||||
|
include::{snippets}/deleteFindingByPentestAndFindingId/path-parameters.adoc[]
|
||||||
|
|
||||||
|
==== Response example
|
||||||
|
|
||||||
|
include::{snippets}/deleteFindingByPentestAndFindingId/http-response.adoc[]
|
||||||
|
|
||||||
|
==== Response structure
|
||||||
|
|
||||||
|
include::{snippets}/deleteFindingByPentestAndFindingId/response-fields.adoc[]
|
||||||
|
|
||||||
== Change History
|
== Change History
|
||||||
|
|
||||||
|===
|
|===
|
||||||
|Date |Change
|
|Date |Change
|
||||||
|2022-12-09
|
|2022-12-09
|
||||||
|
|Added DELETE endpoint for Finding
|
||||||
|
|2022-12-08
|
||||||
|Added GET and PATCH endpoint for Finding
|
|Added GET and PATCH endpoint for Finding
|
||||||
|2022-12-02
|
|2022-12-02
|
||||||
|Added GET and POST endpoint for Findings
|
|Added GET and POST endpoint for Findings
|
||||||
|
|
|
@ -39,4 +39,5 @@ enum class Errorcode(val code: Int) {
|
||||||
PentestInsertionFailed(6007),
|
PentestInsertionFailed(6007),
|
||||||
ProjectPentestInsertionFailed(6008),
|
ProjectPentestInsertionFailed(6008),
|
||||||
FindingInsertionFailed(6009),
|
FindingInsertionFailed(6009),
|
||||||
|
FindingDeletionFailed(6010),
|
||||||
}
|
}
|
|
@ -52,6 +52,12 @@ fun Finding.toFindingResponseBody(): ResponseBody {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Finding.toFindingDeleteResponseBody(): ResponseBody {
|
||||||
|
return mapOf(
|
||||||
|
"id" to id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates if a [FindingRequestBody] is valid
|
* Validates if a [FindingRequestBody] is valid
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.securityc4po.api.finding
|
package com.securityc4po.api.finding
|
||||||
|
|
||||||
|
import org.springframework.data.mongodb.repository.DeleteQuery
|
||||||
import org.springframework.data.mongodb.repository.Query
|
import org.springframework.data.mongodb.repository.Query
|
||||||
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
|
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
|
@ -14,4 +15,7 @@ interface FindingRepository : ReactiveMongoRepository<FindingEntity, String> {
|
||||||
|
|
||||||
@Query("{'data._id' :{\$in: ?0 }}")
|
@Query("{'data._id' :{\$in: ?0 }}")
|
||||||
fun findFindingsByIds(id: List<String>): Flux<FindingEntity>
|
fun findFindingsByIds(id: List<String>): Flux<FindingEntity>
|
||||||
|
|
||||||
|
@DeleteQuery("{'data._id' : ?0}")
|
||||||
|
fun deleteFindingById(id: String): Mono<Long>
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,4 +124,32 @@ class FindingService(private val findingRepository: FindingRepository, private v
|
||||||
throw ex
|
throw ex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteFindingByPentestAndFindingId(pentestId: String, findingId: String): Mono<Finding> {
|
||||||
|
return findingRepository.findFindingById(findingId).switchIfEmpty {
|
||||||
|
logger.info("Finding with id $findingId not found. Deletion not necessary.")
|
||||||
|
Mono.empty()
|
||||||
|
}.flatMap { findingEntity: FindingEntity ->
|
||||||
|
val finding = findingEntity.toFinding()
|
||||||
|
findingRepository.deleteFindingById(findingId).flatMap {
|
||||||
|
// After successfully deleting finding remove its id from pentest
|
||||||
|
pentestService.removeFindingFromPentest(pentestId, findingId).onErrorMap {
|
||||||
|
TransactionInterruptedException(
|
||||||
|
"Pentest could not be updated in Database.",
|
||||||
|
Errorcode.PentestInsertionFailed
|
||||||
|
)
|
||||||
|
}.map {
|
||||||
|
finding
|
||||||
|
}
|
||||||
|
}.doOnError {
|
||||||
|
throw wrappedException(
|
||||||
|
logging = { logger.warn("Finding could not be deleted from Database. Thrown exception: ", it) },
|
||||||
|
mappedException = TransactionInterruptedException(
|
||||||
|
"Finding could not be deleted.",
|
||||||
|
Errorcode.FindingDeletionFailed
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -6,11 +6,13 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||||
import com.securityc4po.api.ResponseBody
|
import com.securityc4po.api.ResponseBody
|
||||||
import com.securityc4po.api.finding.FindingRequestBody
|
import com.securityc4po.api.finding.FindingRequestBody
|
||||||
import com.securityc4po.api.finding.FindingService
|
import com.securityc4po.api.finding.FindingService
|
||||||
|
import com.securityc4po.api.finding.toFindingDeleteResponseBody
|
||||||
import com.securityc4po.api.finding.toFindingResponseBody
|
import com.securityc4po.api.finding.toFindingResponseBody
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.http.ResponseEntity.noContent
|
import org.springframework.http.ResponseEntity.noContent
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
|
import reactor.kotlin.core.publisher.switchIfEmpty
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/pentests")
|
@RequestMapping("/pentests")
|
||||||
|
@ -108,4 +110,16 @@ class PentestController(private val pentestService: PentestService, private val
|
||||||
ResponseEntity.accepted().body(it.toFindingResponseBody())
|
ResponseEntity.accepted().body(it.toFindingResponseBody())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{pentestId}/finding/{findingId}")
|
||||||
|
fun deleteFinding(
|
||||||
|
@PathVariable(value = "pentestId") pentestId: String,
|
||||||
|
@PathVariable(value = "findingId") findingId: String
|
||||||
|
): Mono<ResponseEntity<ResponseBody>> {
|
||||||
|
return this.findingService.deleteFindingByPentestAndFindingId(pentestId, findingId).map {
|
||||||
|
ResponseEntity.ok().body(it.toFindingDeleteResponseBody())
|
||||||
|
}.switchIfEmpty {
|
||||||
|
Mono.just(ResponseEntity.noContent().build<ResponseBody>())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -154,6 +154,40 @@ class PentestService(private val pentestRepository: PentestRepository, private v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update [Pentest] for Finding
|
||||||
|
*
|
||||||
|
* @throws [InvalidModelException] if the [Pentest] is invalid
|
||||||
|
* @throws [TransactionInterruptedException] if the [Pentest] could not be updated
|
||||||
|
* @return updated [Pentest]
|
||||||
|
*/
|
||||||
|
fun removeFindingFromPentest(pentestId: String, findingId: String): Mono<Pentest> {
|
||||||
|
return pentestRepository.findPentestById(pentestId).switchIfEmpty {
|
||||||
|
logger.warn("Pentest with id $pentestId not found. Updating not possible.")
|
||||||
|
val msg = "Pentest with id $pentestId not found."
|
||||||
|
val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound)
|
||||||
|
throw ex
|
||||||
|
}.flatMap { currentPentestEntity: PentestEntity ->
|
||||||
|
if (currentPentestEntity.data.findingIds.find { pentestData -> pentestData == findingId } != null) {
|
||||||
|
val findingIds = currentPentestEntity.data.findingIds.toMutableList()
|
||||||
|
findingIds.remove(findingId)
|
||||||
|
currentPentestEntity.data.findingIds = findingIds.toList()
|
||||||
|
}
|
||||||
|
currentPentestEntity.lastModified = Instant.now()
|
||||||
|
this.pentestRepository.save(currentPentestEntity).map {
|
||||||
|
it.toPentest()
|
||||||
|
}.doOnError {
|
||||||
|
throw wrappedException(
|
||||||
|
logging = { logger.warn("Pentest could not be updated in Database. Thrown exception: ", it) },
|
||||||
|
mappedException = TransactionInterruptedException(
|
||||||
|
"Pentest could not be updated.",
|
||||||
|
Errorcode.PentestInsertionFailed
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all [Finding]Id's by pentestId
|
* Get all [Finding]Id's by pentestId
|
||||||
*
|
*
|
||||||
|
|
|
@ -14,6 +14,4 @@ interface ProjectRepository: ReactiveMongoRepository<ProjectEntity, String> {
|
||||||
|
|
||||||
@DeleteQuery("{'data._id' : ?0}")
|
@DeleteQuery("{'data._id' : ?0}")
|
||||||
fun deleteProjectById(id: String): Mono<Long>
|
fun deleteProjectById(id: String): Mono<Long>
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -261,7 +261,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
),
|
),
|
||||||
PayloadDocumentation.relaxedResponseFields(
|
PayloadDocumentation.relaxedResponseFields(
|
||||||
PayloadDocumentation.fieldWithPath("[].id").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("[].id").type(JsonFieldType.STRING)
|
||||||
.description("The id of the requested pentest"),
|
.description("The id of the requested findings"),
|
||||||
PayloadDocumentation.fieldWithPath("[].severity").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("[].severity").type(JsonFieldType.STRING)
|
||||||
.description("The severity of the finding"),
|
.description("The severity of the finding"),
|
||||||
PayloadDocumentation.fieldWithPath("[].title").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("[].title").type(JsonFieldType.STRING)
|
||||||
|
@ -325,7 +325,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
),
|
),
|
||||||
PayloadDocumentation.relaxedResponseFields(
|
PayloadDocumentation.relaxedResponseFields(
|
||||||
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
||||||
.description("The id of the requested pentest"),
|
.description("The id of the requested finding"),
|
||||||
PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING)
|
||||||
.description("The severity of the finding"),
|
.description("The severity of the finding"),
|
||||||
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
|
||||||
|
@ -386,7 +386,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
),
|
),
|
||||||
PayloadDocumentation.relaxedResponseFields(
|
PayloadDocumentation.relaxedResponseFields(
|
||||||
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
||||||
.description("The id of the requested pentest"),
|
.description("The id of the saved finding"),
|
||||||
PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING)
|
||||||
.description("The severity of the finding"),
|
.description("The severity of the finding"),
|
||||||
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
|
||||||
|
@ -446,7 +446,7 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
),
|
),
|
||||||
PayloadDocumentation.relaxedResponseFields(
|
PayloadDocumentation.relaxedResponseFields(
|
||||||
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
||||||
.description("The id of the requested pentest"),
|
.description("The id of the updated finding"),
|
||||||
PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("severity").type(JsonFieldType.STRING)
|
||||||
.description("The severity of the finding"),
|
.description("The severity of the finding"),
|
||||||
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
|
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
|
||||||
|
@ -477,6 +477,43 @@ class PentestControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class DeleteFinding {
|
||||||
|
@Test
|
||||||
|
fun deleteFindingByPentestAndFindingId() {
|
||||||
|
val pentestId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
||||||
|
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
|
||||||
|
webTestClient.delete()
|
||||||
|
.uri("/pentests/{pentestId}/finding/{findingId}", pentestId, findingId)
|
||||||
|
.header("Authorization", "Bearer $tokenAdmin")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk
|
||||||
|
.expectHeader().doesNotExist("")
|
||||||
|
.expectBody()
|
||||||
|
.consumeWith(
|
||||||
|
WebTestClientRestDocumentation.document(
|
||||||
|
"{methodName}",
|
||||||
|
Preprocessors.preprocessRequest(
|
||||||
|
Preprocessors.prettyPrint(),
|
||||||
|
Preprocessors.modifyUris().removePort(),
|
||||||
|
Preprocessors.removeHeaders("Host", "Content-Length")
|
||||||
|
),
|
||||||
|
Preprocessors.preprocessResponse(
|
||||||
|
Preprocessors.prettyPrint()
|
||||||
|
),
|
||||||
|
RequestDocumentation.relaxedPathParameters(
|
||||||
|
RequestDocumentation.parameterWithName("pentestId").description("The id of the pentest you want to remove the finidng from"),
|
||||||
|
RequestDocumentation.parameterWithName("findingId").description("The id of the finding you want to delete")
|
||||||
|
),
|
||||||
|
PayloadDocumentation.relaxedResponseFields(
|
||||||
|
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
||||||
|
.description("The id of the finding you deleted")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun persistBasicTestScenario() {
|
private fun persistBasicTestScenario() {
|
||||||
// setup test data
|
// setup test data
|
||||||
// Project
|
// Project
|
||||||
|
|
|
@ -281,6 +281,23 @@ class PentestControllerIntTest : BaseIntTest() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class DeleteFinding {
|
||||||
|
@Test
|
||||||
|
fun `delete finding successfully`() {
|
||||||
|
val pentestId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
||||||
|
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
|
||||||
|
webTestClient.delete()
|
||||||
|
.uri("/pentests/{pentestId}/finding/{findingId}", pentestId, findingId)
|
||||||
|
.header("Authorization", "Bearer $tokenAdmin")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk
|
||||||
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
||||||
|
.expectBody()
|
||||||
|
.jsonPath("$.id").isEqualTo("ab62d365-1b1d-4da1-89bc-5496616e220f")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun persistBasicTestScenario() {
|
private fun persistBasicTestScenario() {
|
||||||
// setup test data
|
// setup test data
|
||||||
// project
|
// project
|
||||||
|
|
Loading…
Reference in New Issue