From c1f56860b5c82ab3ab1319c5f13b86093d86a8fe Mon Sep 17 00:00:00 2001 From: TheCGuyGitHub Date: Sun, 7 Jul 2024 22:01:38 +0200 Subject: [PATCH] Added Permission check to the Endpoints and moved token check to AuthUtil.kt --- readme.md | 13 ++++ .../CloudNet_Rest_Module.kt | 65 +++++++++++++------ .../cloudnet_rest_module/utli/AuthUtil.kt | 25 ++++++- .../cloudnet_rest_module/utli/DBManager.kt | 14 ++++ .../cloudnet_rest_module/utli/JsonUtils.kt | 5 +- src/main/resources/openapi/swagger.yaml | 0 6 files changed, 98 insertions(+), 24 deletions(-) create mode 100644 readme.md delete mode 100644 src/main/resources/openapi/swagger.yaml diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..964a898 --- /dev/null +++ b/readme.md @@ -0,0 +1,13 @@ +
+ +![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/TheCGuyGitHub/CloudNet-Rest-Module/gradle.yml) + +
+
+

CloudNet-Rest-Module

+

This is a Rest-API Module for CloudNet

+
+ + + +Note: This is my first time using the CloudNet API and Kotlin! \ No newline at end of file diff --git a/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/CloudNet_Rest_Module.kt b/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/CloudNet_Rest_Module.kt index 5354821..d443601 100644 --- a/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/CloudNet_Rest_Module.kt +++ b/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/CloudNet_Rest_Module.kt @@ -14,11 +14,15 @@ import eu.cloudnetservice.node.ShutdownHandler import eu.cloudnetservice.node.command.CommandProvider import eu.cloudnetservice.node.service.CloudServiceManager import eu.cloudnetservice.driver.provider.ServiceTaskProvider + + import io.github.thecguy.cloudnet_rest_module.commands.rest import io.github.thecguy.cloudnet_rest_module.config.Configuration import io.github.thecguy.cloudnet_rest_module.coroutines.AuthChecker +import io.github.thecguy.cloudnet_rest_module.utli.AuthUtil import io.github.thecguy.cloudnet_rest_module.utli.DBManager import io.github.thecguy.cloudnet_rest_module.utli.JsonUtils + import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.auth.* @@ -29,6 +33,7 @@ import io.ktor.server.response.* import io.ktor.server.routing.* import jakarta.inject.Named import jakarta.inject.Singleton +import kong.unirest.core.json.JSONObject import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.jetbrains.annotations.NotNull @@ -37,11 +42,12 @@ import java.util.* @Singleton class CloudNet_Rest_Module : DriverModule() { - private val dbm = DBManager() - private val jsonUtils = JsonUtils() + private val dbm = DBManager() + private val jsonUtils = JsonUtils() private val authChecker = AuthChecker() + private val authUtil = AuthUtil() @Volatile - private var configuration: Configuration? = null + private var configuration: Configuration? = null @ModuleTask(order = 127, lifecycle = ModuleLifeCycle.LOADED) @@ -79,7 +85,7 @@ class CloudNet_Rest_Module : DriverModule() { ) dbm.dbexecute("CREATE TABLE IF NOT EXISTS cloudnet_rest_users (id SERIAL PRIMARY KEY, user TEXT, password TEXT)") dbm.dbexecute("CREATE TABLE IF NOT EXISTS cloudnet_rest_permission (id SERIAL PRIMARY KEY, user TEXT, permission TEXT)") - dbm.dbexecute("CREATE TABLE IF NOT EXISTS cloudnet_rest_auths (id SERIAL PRIMARY KEY, type TEXT, value TEXT, timestamp TEXT)") + dbm.dbexecute("CREATE TABLE IF NOT EXISTS cloudnet_rest_auths (id SERIAL PRIMARY KEY, type TEXT, value TEXT, timestamp TEXT, user TEXT)") dbm.dbexecute("DELETE FROM cloudnet_rest_auths") authChecker.schedule() } @@ -96,7 +102,7 @@ class CloudNet_Rest_Module : DriverModule() { GlobalScope.launch { main(cloudServiceManager, shutdownHandler, serviceTaskProvider) } - println("Rest API listening on port {configuration!!.restapi_port}!") + println("Rest API listening on port ${configuration!!.restapi_port}!") } @ModuleTask(lifecycle = ModuleLifeCycle.STARTED) fun start(commandProvider: CommandProvider) { @@ -146,20 +152,23 @@ class CloudNet_Rest_Module : DriverModule() { } routing { - swaggerUI(path = "swagger", swaggerFile = "openapi/swagger.yaml") authenticate("auth-basic") { get("/auth") { - call.respond(jsonUtils.token().toString(4)) + call.respond(jsonUtils.token(call.principal()?.name.toString()).toString(4)) } } + //val token = call.request.headers["Authorization"] + //if (authUtil.authToken(token.toString(), "cloudnet.rest.services")) { + //} else { + // call.response.status(HttpStatusCode.Unauthorized) + //} //services get("/services") { val services = jsonUtils.services(cloudServiceManager) - val tokens = dbm.tokens() - val rToken = call.request.headers["Authorization"] - if (tokens.contains(rToken)) { + val token = call.request.headers["Authorization"] + if (authUtil.authToken(token.toString(), "cloudnet.rest.services")) { call.respondText( services.toString(4) .replace("[", "") @@ -168,16 +177,17 @@ class CloudNet_Rest_Module : DriverModule() { } else { call.response.status(HttpStatusCode.Unauthorized) } + } get("/services/{service}") { val services = cloudServiceManager.services().map { it.name() }.toList() val serv = services.contains(call.parameters["service"]) - val tokens = dbm.tokens() - val rToken = call.request.headers["Authorization"] - if (tokens.contains(rToken)) { + + val token = call.request.headers["Authorization"] + if (authUtil.authToken(token.toString(), "cloudnet.rest.service")) { if (serv) { - call.respond(jsonUtils.service(cloudServiceManager, call.parameters["service"].toString()).toString(4)) + call.respond(jsonUtils.service(cloudServiceManager, call.parameters["service"].toString()).toString(4)) } else { call.response.status(HttpStatusCode.NotFound) } @@ -188,9 +198,8 @@ class CloudNet_Rest_Module : DriverModule() { //tasks get("/tasks") { - val tokens = dbm.tokens() - val rToken = call.request.headers["Authorization"] - if (tokens.contains(rToken)) { + val token = call.request.headers["Authorization"] + if (authUtil.authToken(token.toString(), "cloudnet.rest.tasks")) { call.respond(jsonUtils.tasks(serviceTaskProvider).toString(4)) } else { call.response.status(HttpStatusCode.Unauthorized) @@ -200,9 +209,9 @@ class CloudNet_Rest_Module : DriverModule() { get("/tasks/{task}") { val tasks = serviceTaskProvider.serviceTasks().map { it.name() }.toList() val tas = tasks.contains(call.parameters["task"]) - val tokens = dbm.tokens() - val rToken = call.request.headers["Authorization"] - if (tokens.contains(rToken)) { + + val token = call.request.headers["Authorization"] + if (authUtil.authToken(token.toString(), "cloudnet.rest.task")) { if (tas) { call.respond(jsonUtils.task(serviceTaskProvider, call.parameters["task"].toString())) } else { @@ -212,8 +221,22 @@ class CloudNet_Rest_Module : DriverModule() { call.response.status(HttpStatusCode.Unauthorized) } } + delete("/tasks/{task}") { + val tasks = serviceTaskProvider.serviceTasks().map { it.name() }.toList() + val tas = tasks.contains(call.parameters["task"]) - + val token = call.request.headers["Authorization"] + if (authUtil.authToken(token.toString(), "cloudnet.rest.task.delete")) { + if (tas) { + serviceTaskProvider.removeServiceTask(serviceTaskProvider.serviceTask(call.parameters["task"].toString())!!) + call.response.status(HttpStatusCode.OK) + } else { + call.response.status(HttpStatusCode.NotFound) + } + } else { + call.response.status(HttpStatusCode.Unauthorized) + } + } diff --git a/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/utli/AuthUtil.kt b/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/utli/AuthUtil.kt index 61ebf52..c6e5c9f 100644 --- a/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/utli/AuthUtil.kt +++ b/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/utli/AuthUtil.kt @@ -4,7 +4,7 @@ import java.security.SecureRandom class AuthUtil internal constructor() { - + val dbManager = DBManager() init { @@ -24,6 +24,29 @@ class AuthUtil internal constructor() { return token.toString() } + fun authUser(user: String, permission: String) { + + } + + fun authToken(token: String, permission: String): Boolean { + return if (validToken(token)) { + val user = dbManager.tokenToUser(token) + val perms = dbManager.cmd_rest_perms(user) + if (perms.contains(permission) || perms.contains("*")) { + true + } else { + false + } + } else { + false + } + } + + private fun validToken(token: String): Boolean { + val tokens = dbManager.tokens() + return tokens.contains(token) + } + companion object { private var instance: AuthUtil? = null fun getInstance(): AuthUtil { diff --git a/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/utli/DBManager.kt b/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/utli/DBManager.kt index 2b91ae5..cbf049b 100644 --- a/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/utli/DBManager.kt +++ b/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/utli/DBManager.kt @@ -101,6 +101,20 @@ class DBManager internal constructor() { return exprList } + fun tokenToUser(token: String): String { + var user = "" + ds.connection.use { connection -> + connection.prepareStatement("SELECT user FROM cloudnet_rest_auths WHERE value = '$token'").use { statement -> + statement.executeQuery().use { resultSet -> + if (resultSet.next()) { + user = resultSet.getString("user").toString() + } + } + } + } + return user + } + fun cmd_rest_users(): List { diff --git a/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/utli/JsonUtils.kt b/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/utli/JsonUtils.kt index 0d4196c..3ea7fc5 100644 --- a/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/utli/JsonUtils.kt +++ b/src/main/kotlin/io/github/thecguy/cloudnet_rest_module/utli/JsonUtils.kt @@ -2,6 +2,7 @@ package io.github.thecguy.cloudnet_rest_module.utli import eu.cloudnetservice.driver.provider.ServiceTaskProvider import eu.cloudnetservice.node.service.CloudServiceManager +import io.github.thecguy.cloudnet_rest_module.config.Configuration import kong.unirest.core.json.JSONArray import kong.unirest.core.json.JSONObject import org.jetbrains.annotations.NotNull @@ -153,7 +154,7 @@ class JsonUtils internal constructor() { - fun token():JSONObject { + fun token(user: String):JSONObject { val token = JSONObject() val ttoken = authUtil.generateToken(256) val date = Date(System.currentTimeMillis() + 600000) @@ -163,7 +164,7 @@ class JsonUtils internal constructor() { } else { token.put("token", ttoken) token.put("expire_date", date) - dbManager.dbexecute("INSERT INTO cloudnet_rest_auths (type, value, timestamp) VALUES ('QUERY', '$ttoken', '$date')") + dbManager.dbexecute("INSERT INTO cloudnet_rest_auths (type, value, timestamp, user) VALUES ('QUERY', '$ttoken', '$date', '$user')") } println(dbManager.tokensDate()) return token diff --git a/src/main/resources/openapi/swagger.yaml b/src/main/resources/openapi/swagger.yaml deleted file mode 100644 index e69de29..0000000