284 lines
13 KiB
Kotlin
284 lines
13 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 }
|
|
}
|
|
|
|
/**
|
|
* Update [Pentest] for Comment
|
|
*
|
|
* @throws [InvalidModelException] if the [Pentest] is invalid
|
|
* @throws [TransactionInterruptedException] if the [Pentest] could not be updated
|
|
* @return updated [Pentest]
|
|
*/
|
|
fun removeCommentFromPentest(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) {
|
|
val commentIds = currentPentestEntity.data.commentIds.toMutableList()
|
|
commentIds.remove(commentId)
|
|
currentPentestEntity.data.commentIds = commentIds.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
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
} |