feat: As a user I want to get project data by projectId that is filled with all the necessary data for report creation

This commit is contained in:
Marcel Haag 2023-02-17 09:13:08 +01:00
parent 280948c470
commit 88c252dd7e
32 changed files with 434 additions and 136 deletions

View File

@ -20,7 +20,7 @@
(click)="onClickEditPentestProject()"> (click)="onClickEditPentestProject()">
<fa-icon [icon]="fa.faEdit" <fa-icon [icon]="fa.faEdit"
class="element-icon fa-lg"></fa-icon> class="element-icon fa-lg"></fa-icon>
<span class="element-text">{{ 'global.action.edit' | translate }}</span> <!--<span class="element-text">{{ 'global.action.edit' | translate }}</span>-->
</button> </button>
</nb-action> </nb-action>
<nb-action> <nb-action>

View File

@ -71,7 +71,7 @@
</nb-form-field> </nb-form-field>
</div> </div>
<!-- Severity Layout --> <!-- Severity Layout -->
<!-- Severity Form Field --> <!-- Severity Dropdown -->
<div fxFlex class="severity-dialog"> <div fxFlex class="severity-dialog">
<label for="{{formArray[1].fieldName}}" class="label"> <label for="{{formArray[1].fieldName}}" class="label">
{{formArray[1].labelKey | translate}} {{formArray[1].labelKey | translate}}

View File

@ -78,6 +78,7 @@
} }
.severity-0 { .severity-0 {
background-color: nb-theme(color-success-default);
} }
.severity-1 { .severity-1 {
background-color: nb-theme(color-info-default); background-color: nb-theme(color-info-default);

View File

@ -110,7 +110,7 @@ export class FindingDialogComponent implements OnInit {
let severityFillStatus; let severityFillStatus;
switch (value) { switch (value) {
case 0: { case 0: {
severityFillStatus = 'basic'; severityFillStatus = 'success';
break; break;
} }
case 1: { case 1: {
@ -126,7 +126,7 @@ export class FindingDialogComponent implements OnInit {
break; break;
} }
default: { default: {
severityFillStatus = 'control'; severityFillStatus = 'basic';
break; break;
} }
} }

View File

@ -3,7 +3,7 @@
.project-dialog { .project-dialog {
width: 40rem !important; width: 40rem !important;
height: 42.25rem; height: 42.5rem;
.project-dialog-header { .project-dialog-header {
height: 8vh; height: 8vh;
@ -32,6 +32,13 @@
height: 8rem; height: 8rem;
} }
.form-textarea:disabled {
width: 26.75rem !important;
// width: 30rem !important;
background-color: nb-theme(color-basic-transparent-focus);
height: 8rem;
}
.error-text { .error-text {
float: left; float: left;
color: nb-theme(color-danger-default); color: nb-theme(color-danger-default);

View File

@ -1,6 +1,6 @@
<ng-container [ngSwitch]="currentSeverity"> <ng-container [ngSwitch]="currentSeverity">
<nb-tag-list> <nb-tag-list>
<nb-tag *ngSwitchCase="severity.LOW" status="basic" appearance="filled" <nb-tag *ngSwitchCase="severity.LOW" status="success" appearance="filled"
text="{{getTranslationKey() | translate}}"></nb-tag> text="{{getTranslationKey() | translate}}"></nb-tag>
<nb-tag *ngSwitchCase="severity.MEDIUM" status="info" appearance="filled" <nb-tag *ngSwitchCase="severity.MEDIUM" status="info" appearance="filled"
text=" {{getTranslationKey() | translate}}"></nb-tag> text=" {{getTranslationKey() | translate}}"></nb-tag>

View File

@ -53,6 +53,40 @@
}, },
"response": [] "response": []
}, },
{
"name": "getProjectById",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItdG1lbEV0ZHhGTnRSMW9aNXlRdE5jaFFpX0RVN2VNeV9YcU44aXY0S3hzIn0.eyJleHAiOjE2NzY5NzMxMTAsImlhdCI6MTY3Njk3MjgxMCwianRpIjoiNDFkMDAwNzEtNjAyYy00NmEzLThjYjctMTJlZTExYWYyZDBhIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL2M0cG9fcmVhbG1fbG9jYWwiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMTBlMDZkN2EtOGRkMC00ZWNkLTg5NjMtMDU2YjQ1MDc5YzRmIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYzRwb19sb2NhbCIsInNlc3Npb25fc3RhdGUiOiI5MTA5ZWU0Ni03OGEzLTRmMDUtODdhYi03NzIxNGJmNzNlZWMiLCJhbGxvd2VkLW9yaWdpbnMiOlsiKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiYzRwb191c2VyIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImM0cG9fbG9jYWwiOnsicm9sZXMiOlsidXNlciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2lkIjoiOTEwOWVlNDYtNzhhMy00ZjA1LTg3YWItNzcyMTRiZjczZWVjIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoidGVzdCB1c2VyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoidHR0IiwiZ2l2ZW5fbmFtZSI6InRlc3QiLCJmYW1pbHlfbmFtZSI6InVzZXIifQ.hZhUBi4cGQdn3lZ1Xm1Kz2WpboiBBJCFrtODD_c4N0ZiymB0MWVc1jXzU1fQ25mZ_I9VJXqg97x_gnCM7mKJrncFxs6cj75zIeH3so1BhlcDf7q2pjIkCH1yCerPWSLtrK2pWyxTr1GyO1Cp_wqQms_7_rmpzajLzmqBGKF8vd4yAk8kHmBoGBJhRU_gVCsDnIe74in3a032---IgCJ2XA0E5yxP9oBe6_9xPuCsk82YDihbfK1ZEO-9YZt0g1Iv3y30-hG10eflftWJEMSi8Bso4H_2WSJLqy4YRuGQR0EKDiomM0deVCK9IkuaoIsdIZ8kd65YuxSnj-_ue17QTA",
"type": "string"
},
{
"key": "undefined",
"type": "any"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:8443/projects/5a4f126c-9471-43b8-80b9-6eb02b7c35d0",
"protocol": "http",
"host": [
"localhost"
],
"port": "8443",
"path": [
"projects",
"5a4f126c-9471-43b8-80b9-6eb02b7c35d0"
]
}
},
"response": []
},
{ {
"name": "saveProject", "name": "saveProject",
"request": { "request": {
@ -699,6 +733,40 @@
}, },
"response": [] "response": []
}, },
{
"name": "getCompletedPentestById",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItdG1lbEV0ZHhGTnRSMW9aNXlRdE5jaFFpX0RVN2VNeV9YcU44aXY0S3hzIn0.eyJleHAiOjE2NzY5Nzk1NjYsImlhdCI6MTY3Njk3OTI2NiwianRpIjoiNzZlYTI3N2MtNmNiMC00OGQyLTgxNzktMDEyZDE2MWU5M2EzIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL2M0cG9fcmVhbG1fbG9jYWwiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMTBlMDZkN2EtOGRkMC00ZWNkLTg5NjMtMDU2YjQ1MDc5YzRmIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYzRwb19sb2NhbCIsInNlc3Npb25fc3RhdGUiOiI0ZWI0NjQwYi00MmEyLTQwZDQtOGY0Yy1lZDViN2FkNTM3YjciLCJhbGxvd2VkLW9yaWdpbnMiOlsiKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiYzRwb191c2VyIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImM0cG9fbG9jYWwiOnsicm9sZXMiOlsidXNlciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2lkIjoiNGViNDY0MGItNDJhMi00MGQ0LThmNGMtZWQ1YjdhZDUzN2I3IiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoidGVzdCB1c2VyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoidHR0IiwiZ2l2ZW5fbmFtZSI6InRlc3QiLCJmYW1pbHlfbmFtZSI6InVzZXIifQ.utN710Mq5OBUb3FBktvOZSbvRV4Mgsiel0EiNt-1hcgg_J83e_Gc2Szk1L4KZteFPjpWcvwrj_J9JdyECu2H23TbDVvtx9yU3CIFikWjwAH75YK08h0Sqv6aGZza0t7fqMe1PIeN70bV_cIvWGA6OdwwGmE3mrllBcCHrnq7_45APig7jT6-tJplt8Lf2zTf9sMm5PhvqtPL78rLdtgnLXstxr1kAKsbcc8RKOPh65ve1sBMNH8uXgCER8JDPngam5Jt-iZ6m6g_Bmy6RJWFApfFj1mE1dGoVQCsUhGkWZnApIy0PDFGs6UtsNM0pLOxuG9vQjeeq6R_IAq9Zo_3eA",
"type": "string"
},
{
"key": "undefined",
"type": "any"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:8443/pentests/11601f51-bc17-47fd-847d-0c53df5405b5",
"protocol": "http",
"host": [
"localhost"
],
"port": "8443",
"path": [
"pentests",
"11601f51-bc17-47fd-847d-0c53df5405b5"
]
}
},
"response": []
},
{ {
"name": "savePentest", "name": "savePentest",
"request": { "request": {

View File

@ -1,6 +1,8 @@
package com.securityc4po.api.pentest package com.securityc4po.api.pentest
import com.securityc4po.api.ResponseBody import com.securityc4po.api.ResponseBody
import com.securityc4po.api.pentest.comment.Comment
import com.securityc4po.api.pentest.finding.Finding
import org.springframework.data.mongodb.core.index.Indexed import org.springframework.data.mongodb.core.index.Indexed
import java.util.UUID import java.util.UUID
@ -51,6 +53,28 @@ fun Pentest.toPentestResponseBody(): ResponseBody {
) )
} }
data class CompletedPentest(
val id: String,
val projectId: String,
val category: PentestCategory,
val refNumber: String,
val status: PentestStatus,
var findings: MutableList<Finding>,
var comments: MutableList<Comment>
)
fun CompletedPentest.toCompletedPentestResponseBody(): ResponseBody {
return mapOf(
"id" to id,
"projectId" to projectId,
"category" to category,
"refNumber" to refNumber,
"status" to status,
"findings" to findings,
"comments" to comments
)
}
data class PentestRequestBody( data class PentestRequestBody(
val projectId: String, val projectId: String,
val refNumber: String, val refNumber: String,

View File

@ -4,6 +4,8 @@ import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.extensions.getLoggerFor import com.securityc4po.api.extensions.getLoggerFor
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import com.securityc4po.api.ResponseBody import com.securityc4po.api.ResponseBody
import com.securityc4po.api.pentest.comment.CommentService
import com.securityc4po.api.pentest.finding.FindingService
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.*
@ -18,7 +20,7 @@ import reactor.core.publisher.Mono
methods = [RequestMethod.GET, RequestMethod.DELETE, RequestMethod.POST, RequestMethod.PATCH] methods = [RequestMethod.GET, RequestMethod.DELETE, RequestMethod.POST, RequestMethod.PATCH]
) )
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION) @SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION)
class PentestController(private val pentestService: PentestService) { class PentestController(private val pentestService: PentestService, private val pentestReportService: PentestReportService) {
var logger = getLoggerFor<PentestController>() var logger = getLoggerFor<PentestController>()
@ -37,15 +39,14 @@ class PentestController(private val pentestService: PentestService) {
} }
} }
/* Todo: Add API @GetMapping("/{pentestId}")
@GetMapping fun getCompletedPentestById(
fun getPentestById( @PathVariable(value = "pentestId") pentestId: String
@RequestParam("pentestId") pentestId: String ): Mono<ResponseEntity<ResponseBody>> {
): Mono<ResponseEntity<List<ResponseBody>>> { return pentestReportService.getCompletedPentest(pentestId).map {
return pentestService.getPentest(pentestId).map { ResponseEntity.ok(it.toCompletedPentestResponseBody())
ResponseEntity.ok(it)
} }
}*/ }
@PostMapping("/{projectId}") @PostMapping("/{projectId}")
fun savePentest( fun savePentest(

View File

@ -3,6 +3,8 @@ package com.securityc4po.api.pentest
import com.securityc4po.api.BaseEntity import com.securityc4po.api.BaseEntity
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.configuration.MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION import com.securityc4po.api.configuration.MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.pentest.comment.Comment
import com.securityc4po.api.pentest.finding.Finding
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.springframework.data.mongodb.core.mapping.Document import org.springframework.data.mongodb.core.mapping.Document
@ -23,6 +25,18 @@ fun PentestEntity.toPentest(): Pentest {
) )
} }
fun PentestEntity.toCompletedPentest(): CompletedPentest {
return CompletedPentest(
this.data.id,
this.data.projectId,
this.data.category,
this.data.refNumber,
this.data.status,
mutableListOf<Finding>(),
mutableListOf<Comment>()
)
}
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION) @SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION)
fun List<PentestEntity>.toPentests(): List<Pentest> { fun List<PentestEntity>.toPentests(): List<Pentest> {
return this.map { return this.map {

View File

@ -0,0 +1,54 @@
package com.securityc4po.api.pentest
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.configuration.MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.configuration.error.handler.EntityNotFoundException
import com.securityc4po.api.configuration.error.handler.Errorcode
import com.securityc4po.api.extensions.getLoggerFor
import com.securityc4po.api.pentest.comment.CommentService
import com.securityc4po.api.pentest.finding.Finding
import com.securityc4po.api.pentest.finding.FindingService
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers
import reactor.kotlin.core.publisher.switchIfEmpty
import reactor.kotlin.core.publisher.toMono
@Service
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION)
class PentestReportService(
private val pentestRepository: PentestRepository,
private val findingService: FindingService,
private val commentService: CommentService
) {
var logger = getLoggerFor<PentestReportService>()
/**
* Get [CompletedPentest]s by pentestId
*
* @return [CompletedPentest]
*/
fun getCompletedPentest(pentestId: String): Mono<CompletedPentest> {
return pentestRepository.findPentestById(pentestId).publishOn(Schedulers.boundedElastic())
.flatMap { pentestEntity ->
val completedPentest = pentestEntity.toCompletedPentest()
// Add all findings to completed Pentest
this.findingService.getFindingsByIds(pentestEntity.data.findingIds).flatMap { listOfFindings ->
completedPentest.findings.addAll(listOfFindings)
// Add all comments to completed Pentest
this.commentService.getCommentsByIds(pentestEntity.data.commentIds).map { listOfComments ->
completedPentest.comments.addAll(listOfComments)
// Return completed Pentest
return@map completedPentest
}
}
}.switchIfEmpty {
logger.warn("Pentest for id $pentestId not found. Collecting pentest information not possible.")
val msg = "Pentest for id $pentestId not found."
val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound)
throw ex
}
}
}

View File

@ -6,6 +6,8 @@ import com.securityc4po.api.configuration.error.handler.*
import com.securityc4po.api.configuration.error.handler.InvalidModelException import com.securityc4po.api.configuration.error.handler.InvalidModelException
import com.securityc4po.api.configuration.error.handler.TransactionInterruptedException import com.securityc4po.api.configuration.error.handler.TransactionInterruptedException
import com.securityc4po.api.extensions.getLoggerFor import com.securityc4po.api.extensions.getLoggerFor
import com.securityc4po.api.pentest.comment.CommentService
import com.securityc4po.api.pentest.finding.FindingService
import com.securityc4po.api.project.* import com.securityc4po.api.project.*
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -15,7 +17,10 @@ import java.time.Instant
@Service @Service
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION) @SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION)
class PentestService(private val pentestRepository: PentestRepository, private val projectService: ProjectService) { class PentestService(
private val pentestRepository: PentestRepository,
private val projectService: ProjectService
) {
var logger = getLoggerFor<PentestService>() var logger = getLoggerFor<PentestService>()
@ -25,12 +30,7 @@ class PentestService(private val pentestRepository: PentestRepository, private v
* @return list of [Pentest] * @return list of [Pentest]
*/ */
fun getPentestsForCategory(projectId: String, category: PentestCategory): Mono<List<Pentest>> { fun getPentestsForCategory(projectId: String, category: PentestCategory): Mono<List<Pentest>> {
return pentestRepository.findPentestByProjectIdAndCategory(projectId, category)/*.switchIfEmpty { return pentestRepository.findPentestByProjectIdAndCategory(projectId, category).collectList().map {
logger.warn("Pentests for project id $projectId not found. Collecting pentests not possible.")
val msg = "Pentests for project id $projectId not found."
val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound)
throw ex
}*/.collectList().map {
it.map { pentestEntity -> pentestEntity.toPentest() } it.map { pentestEntity -> pentestEntity.toPentest() }
} }
} }
@ -98,7 +98,7 @@ class PentestService(private val pentestRepository: PentestRepository, private v
}.flatMap { currentPentestEntity: PentestEntity -> }.flatMap { currentPentestEntity: PentestEntity ->
currentPentestEntity.lastModified = Instant.now() currentPentestEntity.lastModified = Instant.now()
currentPentestEntity.data = buildPentest(body, currentPentestEntity) currentPentestEntity.data = buildPentest(body, currentPentestEntity)
pentestRepository.save(currentPentestEntity).flatMap {newPentestEntity: PentestEntity -> pentestRepository.save(currentPentestEntity).flatMap { newPentestEntity: PentestEntity ->
val pentest = newPentestEntity.toPentest() val pentest = newPentestEntity.toPentest()
// After successfully saving pentest add id and status to project // After successfully saving pentest add id and status to project
val projectPentest = ProjectPentest(pentestId = pentest.id, status = pentest.status) val projectPentest = ProjectPentest(pentestId = pentest.id, status = pentest.status)
@ -122,6 +122,34 @@ class PentestService(private val pentestRepository: PentestRepository, private v
} }
} }
/**
* Get all [Finding]Id's by pentestId
*
* @return list of [String]
*/
fun getFindingIdsByPentestId(pentestId: String): Mono<List<String>> {
return this.pentestRepository.findPentestById(pentestId).switchIfEmpty {
logger.warn("Pentest with id $pentestId not found. Collecting findings not possible.")
val msg = "Pentest with id $pentestId not found."
val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound)
throw ex
}.map { pentestEntity -> pentestEntity.data.findingIds }
}
/**
* Get all [Comment]Id's by pentestId
*
* @return list of [String]
*/
fun getCommentIdsByPentestId(pentestId: String): Mono<List<String>> {
return this.pentestRepository.findPentestById(pentestId).switchIfEmpty {
logger.warn("Pentest with id $pentestId not found. Collecting comments not possible.")
val msg = "Pentest with id $pentestId not found."
val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound)
throw ex
}.map { pentestEntity -> pentestEntity.data.commentIds }
}
/** /**
* Update [Pentest] for Finding * Update [Pentest] for Finding
* *
@ -188,20 +216,6 @@ class PentestService(private val pentestRepository: PentestRepository, private v
} }
} }
/**
* Get all [Finding]Id's by pentestId
*
* @return list of [String]
*/
fun getFindingIdsByPentestId(pentestId: String): Mono<List<String>> {
return this.pentestRepository.findPentestById(pentestId).switchIfEmpty {
logger.warn("Pentest with id $pentestId not found. Collecting findings not possible.")
val msg = "Pentest with id $pentestId not found."
val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound)
throw ex
}.map { pentestEntity -> pentestEntity.data.findingIds }
}
/** /**
* Update [Pentest] for Comment * Update [Pentest] for Comment
* *
@ -234,20 +248,6 @@ class PentestService(private val pentestRepository: PentestRepository, private v
} }
} }
/**
* Get all [Comment]Id's by pentestId
*
* @return list of [String]
*/
fun getCommentIdsByPentestId(pentestId: String): Mono<List<String>> {
return this.pentestRepository.findPentestById(pentestId).switchIfEmpty {
logger.warn("Pentest with id $pentestId not found. Collecting comments not possible.")
val msg = "Pentest with id $pentestId not found."
val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound)
throw ex
}.map { pentestEntity -> pentestEntity.data.commentIds }
}
/** /**
* Update [Pentest] for Comment * Update [Pentest] for Comment
* *

View File

@ -2,7 +2,10 @@ package com.securityc4po.api.project
import com.fasterxml.jackson.annotation.JsonFormat import com.fasterxml.jackson.annotation.JsonFormat
import com.securityc4po.api.ResponseBody import com.securityc4po.api.ResponseBody
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.configuration.MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.pentest.PentestStatus import com.securityc4po.api.pentest.PentestStatus
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.springframework.data.mongodb.core.index.Indexed import org.springframework.data.mongodb.core.index.Indexed
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode import java.math.RoundingMode
@ -49,6 +52,20 @@ fun Project.toProjectResponseBody(): ResponseBody {
) )
} }
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION)
fun Project.toProjectCompletedPentestResponseBody(): ResponseBody {
return mapOf(
"id" to id,
"client" to client,
"title" to title,
"createdAt" to createdAt,
"tester" to tester,
"summary" to summary,
"projectPentests" to projectPentests.filter { pentest -> pentest.status == PentestStatus.COMPLETED },
"createdBy" to createdBy
)
}
fun Project.toProjectDeleteResponseBody(): ResponseBody { fun Project.toProjectDeleteResponseBody(): ResponseBody {
return mapOf( return mapOf(
"id" to id "id" to id

View File

@ -34,7 +34,17 @@ class ProjectController(private val projectService: ProjectService) {
} }
} }
// ToDo: Add getProjectReportDataById Endpoint with return type ProjectReport @GetMapping("/{projectId}")
fun getProjectById(
@PathVariable(value = "projectId") projectId: String
): Mono<ResponseEntity<ResponseBody>> {
return projectService.getProjectById(projectId).map {
it.toProjectCompletedPentestResponseBody()
}.map {
if (it.isEmpty()) ResponseEntity.noContent().build()
else ResponseEntity.ok(it)
}
}
@PostMapping @PostMapping
fun saveProject( fun saveProject(

View File

@ -35,6 +35,23 @@ class ProjectService(private val projectRepository: ProjectRepository) {
} }
} }
/**
* Get [Project] by id
*
* @throws [EntityNotFoundException] if there is no [Project] in collection
* @return [Project]
*/
fun getProjectById(projectId: String): Mono<Project> {
return projectRepository.findProjectById(projectId).map {
it.toProject()
}.switchIfEmpty {
val msg = "Project not found."
val ex = EntityNotFoundException(msg, Errorcode.ProjectNotFound)
logger.warn(msg, ex)
throw ex
}
}
/** /**
* Save [Project] * Save [Project]
* *

View File

@ -101,6 +101,20 @@
{ {
"name": "getReportPDFforProjectById", "name": "getReportPDFforProjectById",
"request": { "request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItdG1lbEV0ZHhGTnRSMW9aNXlRdE5jaFFpX0RVN2VNeV9YcU44aXY0S3hzIn0.eyJleHAiOjE2NzY5ODY0NTgsImlhdCI6MTY3Njk4NjE1OCwianRpIjoiMzk2NjcwM2UtMTk4My00MWU4LTkyNjAtYjhmNDExOWRmYzYwIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL2M0cG9fcmVhbG1fbG9jYWwiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMTBlMDZkN2EtOGRkMC00ZWNkLTg5NjMtMDU2YjQ1MDc5YzRmIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYzRwb19sb2NhbCIsInNlc3Npb25fc3RhdGUiOiIxZDBmMTk4YS03MTg5LTQ4ZmItYWQ0Ny00NTllNjVkOWU3ZjciLCJhbGxvd2VkLW9yaWdpbnMiOlsiKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiYzRwb191c2VyIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImM0cG9fbG9jYWwiOnsicm9sZXMiOlsidXNlciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2lkIjoiMWQwZjE5OGEtNzE4OS00OGZiLWFkNDctNDU5ZTY1ZDllN2Y3IiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoidGVzdCB1c2VyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoidHR0IiwiZ2l2ZW5fbmFtZSI6InRlc3QiLCJmYW1pbHlfbmFtZSI6InVzZXIifQ.m1aOgFK_8ADYS6EdPLWT_wQNTsptSM9XipQQcttSd1lZKdZu6FgFVRSsW39fdgVLGurKxaglGoNgsywGDeomPS7hJuowzmYIEoDZr13MXCBqX9-YAsEDbwvrGcUI4jVXwKl8E-rTLpl3c5Ckj4tbDeUD3EuLk7yTYbkUnijqwsFlZpwJ_gbjZAfNiZCZpJlvh95cQKvFBbyzP7sfxkYikzpxY-1UHSUpoHBxJaOcJ6hoh-PIQVUw-8mvuoOyd5dMmavl5njvijr716_2loj6B6YHLueHIGlenI5Aob9DEOcL17SXivvcEyM5xTKyfqx3Jt1XY7jQ8mOIT4_iqxTc_w",
"type": "string"
},
{
"key": "undefined",
"type": "any"
}
]
},
"method": "GET", "method": "GET",
"header": [ "header": [
{ {
@ -110,7 +124,7 @@
} }
], ],
"url": { "url": {
"raw": "http://localhost:8444/reports/195809ed-9722-4ad5-a84b-0099a9a01652/pdf", "raw": "http://localhost:8444/reports/5a4f126c-9471-43b8-80b9-6eb02b7c35d0/pdf",
"protocol": "http", "protocol": "http",
"host": [ "host": [
"localhost" "localhost"
@ -118,7 +132,7 @@
"port": "8444", "port": "8444",
"path": [ "path": [
"reports", "reports",
"195809ed-9722-4ad5-a84b-0099a9a01652", "5a4f126c-9471-43b8-80b9-6eb02b7c35d0",
"pdf" "pdf"
] ]
} }

View File

@ -5,7 +5,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails import org.springframework.security.core.userdetails.UserDetails
import java.util.stream.Collectors import java.util.stream.Collectors
class Appuser internal constructor() : UserDetails { class Appuser internal constructor(sub: String, username: String, val token: String) : UserDetails {
override fun getAuthorities(): Collection<GrantedAuthority> { override fun getAuthorities(): Collection<GrantedAuthority> {
return listOf("user").stream().map { return listOf("user").stream().map {

View File

@ -9,20 +9,25 @@ import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.oauth2.jwt.Jwt import org.springframework.security.oauth2.jwt.Jwt
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.toMono
import java.util.stream.Collectors import java.util.stream.Collectors
class AppuserJwtAuthConverter(private val appuserDetailsService: UserAccountDetailsService) : class AppuserJwtAuthConverter :
Converter<Jwt, Mono<AbstractAuthenticationToken>> { Converter<Jwt, Mono<AbstractAuthenticationToken>> {
override fun convert(jwt: Jwt): Mono<AbstractAuthenticationToken> { override fun convert(jwt: Jwt): Mono<AbstractAuthenticationToken> {
val authorities = extractAuthorities(jwt) /*val authorities = extractAuthorities(jwt)
// val sub = extractSub(jwt) // val sub = extractSub(jwt)
val username = extractUserName(jwt) val username = extractUserName(jwt)
return appuserDetailsService return appuserDetailsService
.findByUsername(username) .findByUsername(username)
.map { user -> .map { user ->
UsernamePasswordAuthenticationToken(user, "n/a", authorities); UsernamePasswordAuthenticationToken(user, "n/a", authorities);
} }*/
val authorities = extractAuthorities(jwt)
val sub = extractSub(jwt)
val username = extractUserName(jwt)
return UsernamePasswordAuthenticationToken(Appuser(sub, username, jwt.tokenValue!!), "n/a", authorities).toMono()
} }
private fun extractSub(jwt: Jwt): String { private fun extractSub(jwt: Jwt): String {

View File

@ -1,14 +0,0 @@
package com.securityc4po.reporting.configuration.security
import org.springframework.security.core.userdetails.ReactiveUserDetailsService
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.toMono
@Service
class UserAccountDetailsService : ReactiveUserDetailsService {
override fun findByUsername(username: String): Mono<UserDetails> {
return Appuser().toMono()
}
}

View File

@ -22,7 +22,7 @@ import org.springframework.web.cors.CorsConfiguration
@EnableReactiveMethodSecurity @EnableReactiveMethodSecurity
@Configuration @Configuration
@ComponentScan @ComponentScan
class WebSecurityConfiguration(private val userAccountDetailsService: UserAccountDetailsService) { class WebSecurityConfiguration {
@Value("\${external.issuer-uri}") @Value("\${external.issuer-uri}")
var externalIssuerUri: String? = null var externalIssuerUri: String? = null
@ -59,9 +59,10 @@ class WebSecurityConfiguration(private val userAccountDetailsService: UserAccoun
@Bean @Bean
fun appuserJwtAuthenticationConverter(): AppuserJwtAuthConverter { fun appuserJwtAuthenticationConverter(): AppuserJwtAuthConverter {
return AppuserJwtAuthConverter(userAccountDetailsService) return AppuserJwtAuthConverter()
} }
@Bean @Bean
@Profile("COMPOSE") @Profile("COMPOSE")
fun jwtDecoder(): ReactiveJwtDecoder { fun jwtDecoder(): ReactiveJwtDecoder {

View File

@ -1,6 +1,8 @@
package com.securityc4po.reporting.remote package com.securityc4po.reporting.remote
import com.securityc4po.reporting.http.ApplicationHeaders import com.securityc4po.reporting.http.ApplicationHeaders
import com.securityc4po.reporting.remote.model.PentestReport
import com.securityc4po.reporting.remote.model.api.Project
import com.securityc4po.reporting.remote.model.ProjectReport import com.securityc4po.reporting.remote.model.ProjectReport
import org.springframework.core.ParameterizedTypeReference import org.springframework.core.ParameterizedTypeReference
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@ -28,10 +30,10 @@ class APIClient(
* @param token of String * @param token of String
* @return of [ProjectReport] * @return of [ProjectReport]
*/ */
fun retrieveProjectDataById(projectId: String, token: String): Mono<ProjectReport> { fun retrieveProjectDataById(projectId: String, token: String): Mono<Project> {
val projectByProjectIdUriBuilder = UriComponentsBuilder val projectByProjectIdUriBuilder = UriComponentsBuilder
.fromPath(apiClientCfg.projectReport.path) .fromPath(apiClientCfg.projects.path)
.queryParam("projectId", projectId) .pathSegment(projectId)
return webClient.get() return webClient.get()
.uri(projectByProjectIdUriBuilder.toUriString()) .uri(projectByProjectIdUriBuilder.toUriString())
@ -39,6 +41,27 @@ class APIClient(
it.add(ApplicationHeaders.AUTHORIZATION, "Bearer $token") it.add(ApplicationHeaders.AUTHORIZATION, "Bearer $token")
} }
.retrieve() .retrieve()
.bodyToMono(object : ParameterizedTypeReference<ProjectReport>() {}) .bodyToMono(object : ParameterizedTypeReference<Project>() {})
}
/**
* Retrieves the [PentestReport] data from api service getProjectReportDataById()
*
* @param pentestId String id
* @param token of String
* @return of [PentestReport]
*/
fun retrievePentestDataById(pentestId: String, token: String): Mono<PentestReport> {
val projectByProjectIdUriBuilder = UriComponentsBuilder
.fromPath(apiClientCfg.pentests.path)
.pathSegment(pentestId)
return webClient.get()
.uri(projectByProjectIdUriBuilder.toUriString())
.headers {
it.add(ApplicationHeaders.AUTHORIZATION, "Bearer $token")
}
.retrieve()
.bodyToMono(object : ParameterizedTypeReference<PentestReport>() {})
} }
} }

View File

@ -11,10 +11,7 @@ import java.net.URL
class APIClientCfg { class APIClientCfg {
lateinit var url: URL lateinit var url: URL
var projects = ApiPath() var projects = ApiPath()
var projectReport = ApiPath()
var pentests = ApiPath() var pentests = ApiPath()
val findings = ApiPath()
val comments = ApiPath()
} }
class ApiPath { class ApiPath {

View File

@ -1,20 +1,62 @@
package com.securityc4po.reporting.remote package com.securityc4po.reporting.remote
import com.securityc4po.reporting.remote.model.ProjectReport import com.securityc4po.reporting.extensions.getLoggerFor
import com.securityc4po.reporting.remote.model.*
import com.securityc4po.reporting.remote.model.api.*
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
@Service @Service
class APIService(private val apiClient: APIClient) { class APIService(private val apiClient: APIClient) {
var logger = getLoggerFor<APIService>()
/**
* Requests the complete project report data by project id
*
* @param id of String
* @param token of String
* @return [ProjectReport]
*/
fun requestProjectReportDataById(projectId: String, token: String): Mono<ProjectReport> {
var completedProjectReport: ProjectReport
return this.requestProjectDataById(projectId, token).flatMap { project: Project ->
// Setup completed [ProjectReport] object
completedProjectReport = project.toProjectReport()
// Request completed pentest data and add id to [ProjectReport] object
project.projectPentests?.let {
Flux.fromIterable(it.asIterable()).parallel().flatMap { projectPentest ->
this.requestPentestDataById(projectPentest.pentestId, token).map { completedPentest ->
completedPentest
}
}.sequential().collectList()
}?.map {
completedProjectReport.projectPentestReport.addAll(it)
completedProjectReport
} ?: Mono.just(completedProjectReport)
}
}
/** /**
* Requests the project data by id * Requests the project data by id
* *
* @param id of String * @param id of String
* @param token of String * @param token of String
* @return [ProjectReport] * @return [Project]
*/ */
fun requestProjectDataById(projectId: String, token: String): Mono<ProjectReport> { fun requestProjectDataById(projectId: String, token: String): Mono<Project> {
return apiClient.retrieveProjectDataById(projectId, token) return apiClient.retrieveProjectDataById(projectId, token)
} }
/**
* Requests the pentest report data by pentest id
*
* @param id of String
* @param token of String
* @return [PentestReport]
*/
fun requestPentestDataById(pentestId: String, token: String): Mono<PentestReport> {
return apiClient.retrievePentestDataById(pentestId, token)
}
} }

View File

@ -1,32 +1,14 @@
package com.securityc4po.reporting.remote.model package com.securityc4po.reporting.remote.model
import com.securityc4po.reporting.remote.model.api.Comment
import com.securityc4po.reporting.remote.model.api.Finding
import com.securityc4po.reporting.remote.model.api.PentestStatus
data class PentestReport( data class PentestReport(
val id: String, val id: String,
val category: String, // ToDo: Change to be PentestCategory enum if it can be read by Jasper val category: String,
val refNumber: String, val refNumber: String,
val findings: List<Finding>, val findings: List<Finding>,
val comments: List<Comment>, val comments: List<Comment>,
var status: PentestStatus var status: PentestStatus
) )
enum class PentestStatus {
NOT_STARTED,
DISABLED,
OPEN,
IN_PROGRESS,
COMPLETED
}
enum class PentestCategory {
INFORMATION_GATHERING,
CONFIGURATION_AND_DEPLOY_MANAGEMENT_TESTING,
IDENTITY_MANAGEMENT_TESTING,
AUTHENTICATION_TESTING,
AUTHORIZATION_TESTING,
SESSION_MANAGEMENT_TESTING,
INPUT_VALIDATION_TESTING,
ERROR_HANDLING,
CRYPTOGRAPHY,
BUSINESS_LOGIC_TESTING,
CLIENT_SIDE_TESTING
}

View File

@ -1,16 +1,12 @@
package com.securityc4po.reporting.remote.model package com.securityc4po.reporting.remote.model
import com.fasterxml.jackson.annotation.JsonFormat
import java.time.Instant
data class ProjectReport( data class ProjectReport(
val id: String, val id: String,
val client: String, val client: String,
val title: String, val title: String,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssZ") val createdAt: String,
val createdAt: String = Instant.now().toString(),
val tester: String, val tester: String,
val summary: String? = null, val summary: String? = null,
var projectPentestReport: List<PentestReport> = emptyList(), var projectPentestReport: MutableList<PentestReport> = mutableListOf<PentestReport>(),
val createdBy: String val createdBy: String
) )

View File

@ -1,4 +1,4 @@
package com.securityc4po.reporting.remote.model package com.securityc4po.reporting.remote.model.api
data class Comment ( data class Comment (
val id: String, val id: String,

View File

@ -1,4 +1,4 @@
package com.securityc4po.reporting.remote.model package com.securityc4po.reporting.remote.model.api
data class Finding ( data class Finding (
val id: String, val id: String,

View File

@ -0,0 +1,9 @@
package com.securityc4po.reporting.remote.model.api
enum class PentestStatus {
NOT_STARTED,
DISABLED,
OPEN,
IN_PROGRESS,
COMPLETED
}

View File

@ -0,0 +1,33 @@
package com.securityc4po.reporting.remote.model.api
import com.securityc4po.reporting.remote.model.PentestReport
import com.securityc4po.reporting.remote.model.ProjectReport
data class Project(
val id: String,
val client: String,
val title: String,
val createdAt: String,
val tester: String,
val summary: String? = null,
var projectPentests: List<ProjectPentest>? = emptyList(),
val createdBy: String
)
fun Project.toProjectReport(): ProjectReport {
return ProjectReport(
id = this.id,
client = this.client,
title = this.title,
createdAt = this.createdAt,
tester = this.tester,
summary = this.summary,
projectPentestReport = mutableListOf<PentestReport>(),
createdBy = this.createdBy
)
}
data class ProjectPentest(
val pentestId: String,
var status: String
)

View File

@ -6,7 +6,6 @@ import com.securityc4po.reporting.configuration.security.Appuser
import com.securityc4po.reporting.extensions.getLoggerFor import com.securityc4po.reporting.extensions.getLoggerFor
import com.securityc4po.reporting.remote.APIService import com.securityc4po.reporting.remote.APIService
import com.securityc4po.reporting.remote.model.ProjectReport import com.securityc4po.reporting.remote.model.ProjectReport
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.http.ResponseEntity.notFound import org.springframework.http.ResponseEntity.notFound
@ -34,18 +33,20 @@ class ReportController(private val apiService: APIService, private val reportSer
) )
fun downloadPentestReportPDF(@PathVariable(value = "projectId") projectId: String, @AuthenticationPrincipal user: Appuser): Mono<ResponseEntity<ByteArray>> { fun downloadPentestReportPDF(@PathVariable(value = "projectId") projectId: String, @AuthenticationPrincipal user: Appuser): Mono<ResponseEntity<ByteArray>> {
// Todo: Create Report with Jasper // Todo: Create Report with Jasper
// this.apiService.requestProjectDataById(projectId, user.token) return this.apiService.requestProjectReportDataById(projectId, user.token).flatMap {projectReport ->
val jsonProjectReportString: String = /* ToDo: remove if jsonProjectReportCollection not needed for report generation */
File("./src/test/resources/ProjectReportData.json").readText(Charsets.UTF_8) val jsonProjectReportString: String =
val jsonProjectReportCollection: ProjectReport = File("./src/test/resources/ProjectReportData.json").readText(Charsets.UTF_8)
jacksonObjectMapper().readValue<ProjectReport>(jsonProjectReportString) val jsonProjectReportCollection: ProjectReport =
// Setup headers jacksonObjectMapper().readValue<ProjectReport>(jsonProjectReportString)
return this.reportService.createReport(jsonProjectReportCollection, "pdf").map { reportClassLoaderFilePath -> /* jsonProjectReportCollection */
ResponseEntity.ok().body(reportClassLoaderFilePath) this.reportService.createReport(projectReport, "pdf").map { reportClassLoaderFilePath ->
}.switchIfEmpty { ResponseEntity.ok().body(reportClassLoaderFilePath)
Mono.just(notFound().build<ByteArray>()) }.switchIfEmpty {
}.doOnSuccess { Mono.just(notFound().build<ByteArray>())
this.reportService.cleanUpFiles() }.doOnSuccess {
this.reportService.cleanUpFiles()
}
} }
} }

View File

@ -2,6 +2,8 @@ package com.securityc4po.reporting.report
import com.securityc4po.reporting.extensions.getLoggerFor import com.securityc4po.reporting.extensions.getLoggerFor
import com.securityc4po.reporting.remote.model.* import com.securityc4po.reporting.remote.model.*
import com.securityc4po.reporting.remote.model.api.Comment
import com.securityc4po.reporting.remote.model.api.Finding
import net.sf.jasperreports.engine.* import net.sf.jasperreports.engine.*
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
import org.apache.commons.io.FileUtils import org.apache.commons.io.FileUtils
@ -326,19 +328,16 @@ class ReportService {
for (i in 0 until projectReportCollection.projectPentestReport.size) { for (i in 0 until projectReportCollection.projectPentestReport.size) {
val projectSinglePentestReportDataSource: JRBeanCollectionDataSource = val projectSinglePentestReportDataSource: JRBeanCollectionDataSource =
JRBeanCollectionDataSource(mutableListOf(projectReportCollection.projectPentestReport[i])) JRBeanCollectionDataSource(mutableListOf(projectReportCollection.projectPentestReport[i]))
// Setup Sub-dataset for Findings of Pentest
val pentestFindingsDataSource: JRBeanCollectionDataSource =
JRBeanCollectionDataSource(projectReportCollection.projectPentestReport[i].findings)
// Setup Sub-dataset for Comments of Pentest
// val pentestCommentsDataSource =
// Setup Parameter & add Sub-datasets // Setup Parameter & add Sub-datasets
val parameters = HashMap<String, Any>() val parameters = HashMap<String, Any>()
// Setup Sub-dataset for Findings of Pentest
parameters["PentestFindingsDataSource"] = parameters["PentestFindingsDataSource"] =
if (projectReportCollection.projectPentestReport[i].findings.isNotEmpty()) { if (projectReportCollection.projectPentestReport[i].findings.isNotEmpty()) {
JRBeanCollectionDataSource(projectReportCollection.projectPentestReport[i].findings) JRBeanCollectionDataSource(projectReportCollection.projectPentestReport[i].findings)
} else { } else {
JRBeanCollectionDataSource(emptyList<Finding>()) JRBeanCollectionDataSource(emptyList<Finding>())
} }
// Setup Sub-dataset for Comments of Pentest
parameters["PentestCommentsDataSource"] = parameters["PentestCommentsDataSource"] =
if (projectReportCollection.projectPentestReport[i].comments.isNotEmpty()) { if (projectReportCollection.projectPentestReport[i].comments.isNotEmpty()) {
JRBeanCollectionDataSource(projectReportCollection.projectPentestReport[i].comments) JRBeanCollectionDataSource(projectReportCollection.projectPentestReport[i].comments)

View File

@ -14,10 +14,7 @@ management.endpoints.web.exposure.include=info, health, metrics
## C4PO_ApiService ## ## C4PO_ApiService ##
api.client.url=http://localhost:8443/ api.client.url=http://localhost:8443/
api.client.projects.path=projects api.client.projects.path=projects
api.client.projectReport.path=projects/report
api.client.pentests.path=pentests api.client.pentests.path=pentests
api.client.findings.path=pentests/findings
api.client.comments.path=pentests/comments
## IdentityProvider (Keycloak) ## ## IdentityProvider (Keycloak) ##
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/auth/realms/c4po_realm_local spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/auth/realms/c4po_realm_local