security-c4po/security-c4po-api/src/main/kotlin/com/securityc4po/api/pentest/PentestService.kt

250 lines
11 KiB
Kotlin

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.*
import com.securityc4po.api.configuration.error.handler.InvalidModelException
import com.securityc4po.api.configuration.error.handler.TransactionInterruptedException
import com.securityc4po.api.extensions.getLoggerFor
import com.securityc4po.api.project.*
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.switchIfEmpty
import java.time.Instant
@Service
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION)
class PentestService(private val pentestRepository: PentestRepository, private val projectService: ProjectService) {
var logger = getLoggerFor<PentestService>()
/**
* Get all [Pentest]s by projectId and category
*
* @return list of [Pentest]
*/
fun getPentestsForCategory(projectId: String, category: PentestCategory): Mono<List<Pentest>> {
return pentestRepository.findPentestByProjectIdAndCategory(projectId, category)/*.switchIfEmpty {
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() }
}
}
/**
* Save [Pentest]
*
* @throws [InvalidModelException] if the [Pentest] is invalid
* @throws [TransactionInterruptedException] if the [Pentest] could not be stored
* @return saved [Pentest]
*/
fun savePentest(projectId: String, body: PentestRequestBody): Mono<Pentest> {
validate(
require = body.isValid(),
logging = { logger.warn("Pentest not valid.") },
mappedException = InvalidModelException(
"Pentest not valid.", Errorcode.PentestInvalid
)
)
val pentest = body.toPentest()
val pentestEntity = PentestEntity(pentest)
return pentestRepository.insert(pentestEntity).flatMap { newPentestEntity: PentestEntity ->
val newPentest = newPentestEntity.toPentest()
// After successfully saving pentest add id and status to project
val projectPentest = ProjectPentest(pentestId = newPentest.id, status = newPentest.status)
projectService.updateProjectTestingProgress(projectId, projectPentest).onErrorMap {
TransactionInterruptedException(
"Project Pentests could not be updated in Database.",
Errorcode.ProjectPentestInsertionFailed
)
}.map {
newPentest
}
}.doOnError {
throw wrappedException(
logging = { logger.warn("Pentest could not be stored in Database. Thrown exception: ", it) },
mappedException = TransactionInterruptedException(
"Pentest could not be stored.",
Errorcode.PentestInsertionFailed
)
)
}
}
/**
* Update [Pentest]
*
* @throws [InvalidModelException] if the [Pentest] is invalid
* @throws [TransactionInterruptedException] if the [Pentest] could not be updated
* @return updated [Pentest]
*/
fun updatePentest(pentestId: String, body: PentestRequestBody): Mono<Pentest> {
validate(
require = body.isValid(),
logging = { logger.warn("Pentest not valid.") },
mappedException = InvalidModelException(
"Pentest not valid.", Errorcode.PentestInvalid
)
)
return pentestRepository.findPentestById(pentestId).switchIfEmpty {
logger.warn("Pentest with id $pentestId not found. Updating not possible.")
val msg = "Pentest with id $pentestId not found."
val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound)
throw ex
}.flatMap { currentPentestEntity: PentestEntity ->
currentPentestEntity.lastModified = Instant.now()
currentPentestEntity.data = buildPentest(body, currentPentestEntity)
pentestRepository.save(currentPentestEntity).flatMap {newPentestEntity: PentestEntity ->
val pentest = newPentestEntity.toPentest()
// After successfully saving pentest add id and status to project
val projectPentest = ProjectPentest(pentestId = pentest.id, status = pentest.status)
projectService.updateProjectTestingProgress(body.projectId, projectPentest).onErrorMap {
TransactionInterruptedException(
"Project Pentest could not be updated in Database.",
Errorcode.ProjectPentestInsertionFailed
)
}.map {
return@map newPentestEntity.toPentest()
}
}.doOnError {
throw wrappedException(
logging = { logger.warn("Pentest could not be updated in Database. Thrown exception: ", it) },
mappedException = TransactionInterruptedException(
"Pentest could not be updated.",
Errorcode.PentestInsertionFailed
)
)
}
}
}
/**
* Update [Pentest] for Finding
*
* @throws [InvalidModelException] if the [Pentest] is invalid
* @throws [TransactionInterruptedException] if the [Pentest] could not be updated
* @return updated [Pentest]
*/
fun updatePentestFinding(pentestId: String, findingId: String): Mono<Pentest> {
return pentestRepository.findPentestById(pentestId).switchIfEmpty {
logger.warn("Pentest with id $pentestId not found. Updating not possible.")
val msg = "Pentest with id $pentestId not found."
val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound)
throw ex
}.flatMap { currentPentestEntity: PentestEntity ->
if (currentPentestEntity.data.findingIds.find { pentestData -> pentestData == findingId } == null) {
currentPentestEntity.data.findingIds += findingId
}
currentPentestEntity.lastModified = Instant.now()
this.pentestRepository.save(currentPentestEntity).map {
it.toPentest()
}.doOnError {
throw wrappedException(
logging = { logger.warn("Pentest could not be updated in Database. Thrown exception: ", it) },
mappedException = TransactionInterruptedException(
"Pentest could not be updated.",
Errorcode.PentestInsertionFailed
)
)
}
}
}
/**
* Update [Pentest] for Finding
*
* @throws [InvalidModelException] if the [Pentest] is invalid
* @throws [TransactionInterruptedException] if the [Pentest] could not be updated
* @return updated [Pentest]
*/
fun removeFindingFromPentest(pentestId: String, findingId: String): Mono<Pentest> {
return pentestRepository.findPentestById(pentestId).switchIfEmpty {
logger.warn("Pentest with id $pentestId not found. Updating not possible.")
val msg = "Pentest with id $pentestId not found."
val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound)
throw ex
}.flatMap { currentPentestEntity: PentestEntity ->
if (currentPentestEntity.data.findingIds.find { pentestData -> pentestData == findingId } != null) {
val findingIds = currentPentestEntity.data.findingIds.toMutableList()
findingIds.remove(findingId)
currentPentestEntity.data.findingIds = findingIds.toList()
}
currentPentestEntity.lastModified = Instant.now()
this.pentestRepository.save(currentPentestEntity).map {
it.toPentest()
}.doOnError {
throw wrappedException(
logging = { logger.warn("Pentest could not be updated in Database. Thrown exception: ", it) },
mappedException = TransactionInterruptedException(
"Pentest could not be updated.",
Errorcode.PentestInsertionFailed
)
)
}
}
}
/**
* Get all [Finding]Id's by pentestId
*
* @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
*
* @throws [InvalidModelException] if the [Pentest] is invalid
* @throws [TransactionInterruptedException] if the [Pentest] could not be updated
* @return updated [Pentest]
*/
fun updatePentestComment(pentestId: String, commentId: String): Mono<Pentest> {
return pentestRepository.findPentestById(pentestId).switchIfEmpty {
logger.warn("Pentest with id $pentestId not found. Updating not possible.")
val msg = "Pentest with id $pentestId not found."
val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound)
throw ex
}.flatMap { currentPentestEntity: PentestEntity ->
if (currentPentestEntity.data.commentIds.find { pentestData -> pentestData == commentId } == null) {
currentPentestEntity.data.commentIds += commentId
}
currentPentestEntity.lastModified = Instant.now()
this.pentestRepository.save(currentPentestEntity).map {
it.toPentest()
}.doOnError {
throw wrappedException(
logging = { logger.warn("Pentest could not be updated in Database. Thrown exception: ", it) },
mappedException = TransactionInterruptedException(
"Pentest could not be updated.",
Errorcode.PentestInsertionFailed
)
)
}
}
}
/**
* Get all [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 }
}
}