148 lines
5.3 KiB
Kotlin
148 lines
5.3 KiB
Kotlin
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")
|
|
}
|
|
}
|
|
}
|