mirror of
https://github.com/LamGC/ScalaBot.git
synced 2025-04-29 22:27:31 +00:00
feat(metrics): 运行指标服务端支持设置 HTTP 认证.
支持对运行指标服务端设置 HTTP 认证, 以防止运行指标被非法获取.
This commit is contained in:
parent
c5fe96c02d
commit
215a4670db
@ -5,10 +5,7 @@ import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import mu.KotlinLogging
|
||||
import net.lamgc.scalabot.util.ArtifactSerializer
|
||||
import net.lamgc.scalabot.util.AuthenticationSerializer
|
||||
import net.lamgc.scalabot.util.MavenRepositoryConfigSerializer
|
||||
import net.lamgc.scalabot.util.ProxyTypeSerializer
|
||||
import net.lamgc.scalabot.util.*
|
||||
import org.eclipse.aether.artifact.Artifact
|
||||
import org.eclipse.aether.repository.Authentication
|
||||
import org.eclipse.aether.repository.Proxy
|
||||
@ -92,7 +89,8 @@ internal data class ProxyConfig(
|
||||
internal data class MetricsConfig(
|
||||
val enable: Boolean = false,
|
||||
val port: Int = 9386,
|
||||
val bindAddress: String? = "0.0.0.0"
|
||||
val bindAddress: String? = "0.0.0.0",
|
||||
val authenticator: UsernameAuthenticator? = null
|
||||
)
|
||||
|
||||
/**
|
||||
@ -322,6 +320,7 @@ private object GsonConst {
|
||||
.registerTypeAdapter(DefaultBotOptions.ProxyType::class.java, ProxyTypeSerializer)
|
||||
.registerTypeAdapter(MavenRepositoryConfig::class.java, MavenRepositoryConfigSerializer)
|
||||
.registerTypeAdapter(Authentication::class.java, AuthenticationSerializer)
|
||||
.registerTypeAdapter(UsernameAuthenticator::class.java, UsernameAuthenticatorSerializer)
|
||||
.create()
|
||||
|
||||
val botConfigGson: Gson = baseGson.newBuilder()
|
||||
|
@ -39,14 +39,15 @@ fun main(args: Array<String>): Unit = runBlocking {
|
||||
* 启动运行指标服务器.
|
||||
* 使用 Prometheus 指标格式.
|
||||
*/
|
||||
internal fun startMetricsServer(config: MetricsConfig = Const.config.metrics) {
|
||||
internal fun startMetricsServer(config: MetricsConfig = Const.config.metrics): HTTPServer? {
|
||||
if (!config.enable) {
|
||||
log.debug { "运行指标服务器已禁用." }
|
||||
return
|
||||
return null
|
||||
}
|
||||
|
||||
val builder = HTTPServer.Builder()
|
||||
.withDaemonThreads(true)
|
||||
.withAuthenticator(config.authenticator)
|
||||
.withPort(config.port)
|
||||
.withHostname(config.bindAddress)
|
||||
|
||||
@ -54,6 +55,7 @@ internal fun startMetricsServer(config: MetricsConfig = Const.config.metrics) {
|
||||
.build()
|
||||
.registerShutdownHook()
|
||||
log.info { "运行指标服务器已启动. (Port: ${httpServer.port})" }
|
||||
return httpServer
|
||||
}
|
||||
|
||||
internal class Launcher(private val config: AppConfig = Const.config) : AutoCloseable {
|
||||
|
65
scalabot-app/src/main/kotlin/util/UsernameAuthenticator.kt
Normal file
65
scalabot-app/src/main/kotlin/util/UsernameAuthenticator.kt
Normal file
@ -0,0 +1,65 @@
|
||||
package net.lamgc.scalabot.util
|
||||
|
||||
import com.google.gson.*
|
||||
import com.sun.net.httpserver.BasicAuthenticator
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class UsernameAuthenticator(private val username: String, private val password: String) :
|
||||
BasicAuthenticator("metrics") {
|
||||
override fun checkCredentials(username: String?, password: String?): Boolean =
|
||||
this.username == username && this.password == password
|
||||
|
||||
fun toJsonObject(): JsonObject = JsonObject().apply {
|
||||
addProperty("username", username)
|
||||
addProperty("password", password)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is UsernameAuthenticator && this.username == other.username && this.password == other.password
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = username.hashCode()
|
||||
result = 31 * result + password.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object UsernameAuthenticatorSerializer : JsonSerializer<UsernameAuthenticator>,
|
||||
JsonDeserializer<UsernameAuthenticator> {
|
||||
|
||||
override fun serialize(
|
||||
src: UsernameAuthenticator,
|
||||
typeOfSrc: Type?,
|
||||
context: JsonSerializationContext?
|
||||
): JsonElement {
|
||||
return src.toJsonObject()
|
||||
}
|
||||
|
||||
override fun deserialize(
|
||||
json: JsonElement,
|
||||
typeOfT: Type?,
|
||||
context: JsonDeserializationContext?
|
||||
): UsernameAuthenticator? {
|
||||
if (json.isJsonNull) {
|
||||
return null
|
||||
} else if (!json.isJsonObject) {
|
||||
throw JsonParseException("Invalid attribute value type.")
|
||||
}
|
||||
|
||||
val jsonObj = json.asJsonObject
|
||||
|
||||
if (jsonObj["username"]?.isJsonPrimitive != true) {
|
||||
throw JsonParseException("Invalid attribute value: username")
|
||||
} else if (jsonObj["password"]?.isJsonPrimitive != true) {
|
||||
throw JsonParseException("Invalid attribute value: password")
|
||||
}
|
||||
|
||||
if (jsonObj["username"].asString.isEmpty() || jsonObj["password"].asString.isEmpty()) {
|
||||
throw JsonParseException("`username` or `password` is empty.")
|
||||
}
|
||||
return UsernameAuthenticator(jsonObj["username"].asString, jsonObj["password"].asString)
|
||||
}
|
||||
|
||||
}
|
114
scalabot-app/src/test/kotlin/util/UsernameAuthenticatorTest.kt
Normal file
114
scalabot-app/src/test/kotlin/util/UsernameAuthenticatorTest.kt
Normal file
@ -0,0 +1,114 @@
|
||||
package net.lamgc.scalabot.util
|
||||
|
||||
import com.google.gson.*
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import kotlin.test.*
|
||||
|
||||
class UsernameAuthenticatorTest {
|
||||
|
||||
@Test
|
||||
fun checkCredentialsTest() {
|
||||
val authenticator = UsernameAuthenticator("testUser", "testPassword")
|
||||
assertTrue(authenticator.checkCredentials("testUser", "testPassword"))
|
||||
assertFalse(authenticator.checkCredentials("falseUser", "testPassword"))
|
||||
assertFalse(authenticator.checkCredentials("testUser", "falsePassword"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toJsonObjectTest() {
|
||||
val authenticator = UsernameAuthenticator("testUser", "testPassword")
|
||||
val jsonObject = authenticator.toJsonObject()
|
||||
assertEquals("testUser", jsonObject["username"]?.asString)
|
||||
assertEquals("testPassword", jsonObject["password"]?.asString)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun equalsTest() {
|
||||
val authenticator = UsernameAuthenticator("testUser", "testPassword")
|
||||
assertEquals(authenticator, UsernameAuthenticator("testUser", "testPassword"))
|
||||
assertEquals(authenticator.hashCode(), UsernameAuthenticator("testUser", "testPassword").hashCode())
|
||||
assertNotEquals(authenticator, UsernameAuthenticator("testUser", "falsePassword"))
|
||||
assertNotEquals(authenticator.hashCode(), UsernameAuthenticator("testUser", "falsePassword").hashCode())
|
||||
assertNotEquals(authenticator, UsernameAuthenticator("falseUser", "testPassword"))
|
||||
assertNotEquals(authenticator.hashCode(), UsernameAuthenticator("falseUser", "testPassword").hashCode())
|
||||
assertNotEquals(authenticator, UsernameAuthenticator("falseUser", "falsePassword"))
|
||||
assertNotEquals(authenticator.hashCode(), UsernameAuthenticator("falseUser", "falsePassword").hashCode())
|
||||
assertFalse(authenticator.equals(null))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class UsernameAuthenticatorSerializerTest {
|
||||
|
||||
@Test
|
||||
fun serializeTest() {
|
||||
val authenticator = UsernameAuthenticator("testUser", "testPassword")
|
||||
val jsonElement = UsernameAuthenticatorSerializer.serialize(authenticator, null, null)
|
||||
assertTrue(jsonElement.isJsonObject)
|
||||
val jsonObject = jsonElement.asJsonObject
|
||||
assertEquals("testUser", jsonObject["username"]?.asString)
|
||||
assertEquals("testPassword", jsonObject["password"]?.asString)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deserializeTest() {
|
||||
assertThrows<JsonParseException> {
|
||||
UsernameAuthenticatorSerializer.deserialize(JsonArray(), null, null)
|
||||
}
|
||||
assertThrows<JsonParseException> {
|
||||
UsernameAuthenticatorSerializer.deserialize(JsonPrimitive(""), null, null)
|
||||
}
|
||||
assertNull(UsernameAuthenticatorSerializer.deserialize(JsonNull.INSTANCE, null, null))
|
||||
assertThrows<JsonParseException> {
|
||||
UsernameAuthenticatorSerializer.deserialize(JsonObject().apply {
|
||||
addProperty("username", "testUser")
|
||||
}, null, null)
|
||||
}
|
||||
assertThrows<JsonParseException> {
|
||||
UsernameAuthenticatorSerializer.deserialize(JsonObject().apply {
|
||||
addProperty("username", "testUser")
|
||||
add("password", JsonArray())
|
||||
}, null, null)
|
||||
}
|
||||
assertThrows<JsonParseException> {
|
||||
UsernameAuthenticatorSerializer.deserialize(JsonObject().apply {
|
||||
addProperty("password", "testPassword")
|
||||
}, null, null)
|
||||
}
|
||||
assertThrows<JsonParseException> {
|
||||
UsernameAuthenticatorSerializer.deserialize(JsonObject().apply {
|
||||
add("username", JsonArray())
|
||||
addProperty("password", "testPassword")
|
||||
}, null, null)
|
||||
}
|
||||
assertThrows<JsonParseException> {
|
||||
UsernameAuthenticatorSerializer.deserialize(JsonObject().apply {
|
||||
addProperty("username", "")
|
||||
addProperty("password", "")
|
||||
}, null, null)
|
||||
}
|
||||
assertThrows<JsonParseException> {
|
||||
UsernameAuthenticatorSerializer.deserialize(JsonObject().apply {
|
||||
addProperty("username", "testUser")
|
||||
addProperty("password", "")
|
||||
}, null, null)
|
||||
}
|
||||
assertThrows<JsonParseException> {
|
||||
UsernameAuthenticatorSerializer.deserialize(JsonObject().apply {
|
||||
addProperty("username", "")
|
||||
addProperty("password", "testPassword")
|
||||
}, null, null)
|
||||
}
|
||||
|
||||
val authenticator = UsernameAuthenticatorSerializer.deserialize(JsonObject().apply {
|
||||
addProperty("username", "testUser")
|
||||
addProperty("password", "testPassword")
|
||||
}, null, null)
|
||||
assertNotNull(authenticator)
|
||||
|
||||
assertTrue(authenticator.checkCredentials("testUser", "testPassword"))
|
||||
assertFalse(authenticator.checkCredentials("falseUser", "testPassword"))
|
||||
assertFalse(authenticator.checkCredentials("testUser", "falsePassword"))
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user