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:
parent
280948c470
commit
88c252dd7e
|
@ -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>
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
*
|
*
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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]
|
||||||
*
|
*
|
||||||
|
|
|
@ -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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>() {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -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,
|
|
@ -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,
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.securityc4po.reporting.remote.model.api
|
||||||
|
|
||||||
|
enum class PentestStatus {
|
||||||
|
NOT_STARTED,
|
||||||
|
DISABLED,
|
||||||
|
OPEN,
|
||||||
|
IN_PROGRESS,
|
||||||
|
COMPLETED
|
||||||
|
}
|
|
@ -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
|
||||||
|
)
|
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue