Test all security of routes #76

This commit is contained in:
2021-03-31 02:30:47 +02:00
parent 9f13213a35
commit f17277c0e9
2 changed files with 147 additions and 6 deletions

View File

@@ -124,8 +124,6 @@ paths:
parameters:
- $ref: '#/components/parameters/article'
get:
security:
- JWTAuth: []
summary: Get one article
tags:
- article
@@ -1138,8 +1136,6 @@ paths:
/workgroups:
get:
summary: Get all Workgroup (Paginated)
security:
- JWTAuth: [ ]
tags:
- workgroup
parameters:
@@ -1206,8 +1202,6 @@ paths:
- $ref: '#/components/parameters/workgroup'
get:
summary: Get one workgroup by ID
security:
- JWTAuth: [ ]
tags:
- workgroup
responses:

View File

@@ -0,0 +1,147 @@
package integration
import fr.dcproject.common.utils.getResource
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Tags
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.openapi4j.core.model.OAIContext
import org.openapi4j.parser.OpenApi3Parser
import org.openapi4j.parser.model.v3.OpenApi3
import org.openapi4j.parser.model.v3.Operation
import org.openapi4j.parser.model.v3.Parameter
import org.openapi4j.parser.model.v3.Path
import java.io.File
import java.util.UUID
import kotlin.test.assertTrue
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Tags(Tag("integration"), Tag("auth"))
class `Check auth on all routes` : BaseTest() {
@Test
fun `Check all routes`() {
val filePath = "/openapi.yaml"
OpenApi3Parser().parse(File(filePath.getResource().toURI()), true).let { api: OpenApi3 ->
/* Loop on paths and http methods */
api.paths.flatMap { (pathName: String, path: Path) ->
path.operations
/* Take only the secure route */
.filter { (_, operation: Operation) -> operation.hasSecurityRequirements() }
.map { (methodName, _) ->
/* Send request to check security */
sendRequest(
path.buildUrl(pathName, methodName, api.context), /* Replace route to real URL */
HttpMethod.parse(methodName.toUpperCase()) /* Convert http method name to enum */
)
}
}.let { requests ->
/* Check security of routes */
assertTrue(
requests.all { it.statusCode == HttpStatusCode.Forbidden },
requests
.filter { it.statusCode != HttpStatusCode.Forbidden }
.joinToString("\n") { it.toString() }
)
}
}
}
private fun sendRequest(uri: String, method: HttpMethod): RequestResponse {
return try {
withIntegrationApplication {
handleRequest(true) {
this.method = method
this.uri = uri
addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
addHeader(HttpHeaders.Accept, ContentType.Application.Json.toString())
}.run {
RequestResponse(
response.status() ?: error("Request error"),
method,
uri
)
}
}
} catch (e: Throwable) {
RequestResponse(
HttpStatusCode.InternalServerError,
method,
uri
)
}
}
private data class RequestResponse(
val statusCode: HttpStatusCode,
val method: HttpMethod,
val uri: String
) {
override fun toString(): String {
return """HttpStatus ${statusCode.value} for: ${method.value.padStart(6, ' ')} $uri"""
}
}
}
private fun Path.buildUrl(path: String, methodName: String, context: OAIContext): String {
val urlReplaced = this.getParametersIn(context, "path")
.fold(path) { pathToReplace: String, parameter: Parameter ->
"""\{${parameter.name}}""".toRegex().replace(
pathToReplace,
parameter.generateFakeValue()
)
}
val rootQueryParameters = this.getParametersIn(context, "query")
.filter { it.isRequired }
.map { parameter ->
parameter
.generateFakeArray()
.joinToString("&") { "${parameter.name}=$it" }
}
val queryParameters = this.getOperation(methodName).getParametersIn(context, "query")
.filter { it.isRequired }
.map { parameter ->
parameter
.generateFakeArray()
.joinToString("&") { "${parameter.name}=$it" }
}
val allParameters: String = (rootQueryParameters + queryParameters)
.joinToString("&")
.let {
if (it.isNotEmpty()) {
"?$it"
} else {
it
}
}
return "$urlReplaced$allParameters"
}
private fun Parameter.generateFakeValue(): String {
return if (example != null) {
example.toString()
} else if (schema.type == "string" && schema.format == "uuid") {
UUID.randomUUID().toString()
} else {
"example123"
}
}
private fun Parameter.generateFakeArray(): List<String> {
if (schema.type != "array") {
error("Parameter is not an array")
}
return if (example != null && example is Iterable<*>) {
(example as Iterable<*>).map { it.toString() }
} else if (schema.itemsSchema.type == "string" && schema.itemsSchema.format == "uuid") {
listOf(UUID.randomUUID().toString())
} else {
listOf("example123")
}
}