package com.securityc4po.api.project import com.fasterxml.jackson.annotation.JsonFormat 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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings import org.springframework.data.mongodb.core.index.Indexed import java.math.BigDecimal import java.math.RoundingMode import java.time.Instant import java.util.UUID data class Project( @Indexed(background = true, unique = true) val id: String = UUID.randomUUID().toString(), val client: String, val title: String, @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssZ") val createdAt: String = Instant.now().toString(), val tester: String, val summary: String? = null, val state: PentestState, val version: String, var projectPentests: List = emptyList(), @Indexed(background = true, unique = false) var createdBy: String ) fun buildProject(body: ProjectRequestBody, projectEntity: ProjectEntity): Project { return Project( id = projectEntity.data.id, client = body.client, title = body.title, createdAt = projectEntity.data.createdAt, tester = body.tester, summary = body.summary, state = body.state, version = projectEntity.data.version, projectPentests = projectEntity.data.projectPentests, createdBy = projectEntity.data.createdBy ) } fun Project.toProjectResponseBody(): ResponseBody { return mapOf( "id" to id, "client" to client, "title" to title, "createdAt" to createdAt, "tester" to tester, "summary" to summary, "state" to state, "version" to version, /* ToDo: Calculate percentage in BE type: float */ "testingProgress" to calculateProgress(), "createdBy" to createdBy ) } @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, "version" to version, "projectPentests" to projectPentests.filter { pentest -> pentest.status == PentestStatus.COMPLETED }, "createdBy" to createdBy ) } @SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION) fun Project.toProjectEvaluatedPentestResponseBody(): ResponseBody { return mapOf( "id" to id, "client" to client, "title" to title, "createdAt" to createdAt, "tester" to tester, "summary" to summary, "version" to version, "projectPentests" to projectPentests, "createdBy" to createdBy ) } fun Project.toProjectDeleteResponseBody(): ResponseBody { return mapOf( "id" to id ) } fun Project.calculateProgress(): BigDecimal { // Total number of pentests listet in the OWASP testing guide // https://owasp.org/www-project-web-security-testing-guide/assets/archive/OWASP_Testing_Guide_v4.pdf // @Value("\${owasp.web.objectives}") // lateinit var TOTALPENTESTS: Int var TOTAL_OWASP_OBJECTIVES = 95.0 return if (projectPentests.isEmpty()) BigDecimal.ZERO else { var completedPentests = 0.0 projectPentests.forEach { projectPentest -> if (projectPentest.status == PentestStatus.COMPLETED) { completedPentests += 1.0 } else if (projectPentest.status == PentestStatus.DISABLED) { TOTAL_OWASP_OBJECTIVES -= 1 } else if (projectPentest.status != PentestStatus.NOT_STARTED) { completedPentests += 0.5 } } val progress = (completedPentests * 100) / TOTAL_OWASP_OBJECTIVES BigDecimal(progress).setScale(2, RoundingMode.HALF_UP) } } data class ProjectOverview( val projects: List ) data class ProjectRequestBody( val client: String, val title: String, val tester: String, val state: PentestState, val summary: String? ) /** * Validates if a [ProjectRequestBody] is valid * * @return Boolean describing if the body is valid */ fun ProjectRequestBody.isValid(): Boolean { return when { this.client.isBlank() -> false this.title.isBlank() -> false this.tester.isBlank() -> false this.state.toString().isBlank() -> false else -> true } } fun ProjectRequestBody.toProject(): Project { return Project( id = UUID.randomUUID().toString(), client = this.client, title = this.title, createdAt = Instant.now().toString(), tester = this.tester, summary = this.summary, state = this.state, // ToDo: Update version in backend automatically version = "1.0", // ToDo: Should be changed to SUB from Token after adding AUTH Header createdBy = "" ) } fun ProjectRequestBody.toNewProject(userId: String): Project { return Project( id = UUID.randomUUID().toString(), client = this.client, title = this.title, createdAt = Instant.now().toString(), tester = this.tester, summary = this.summary, state = this.state, // ToDo: Update version in backend automatically version = "1.0", createdBy = userId ) }