refactor: 调整 Utils 的多个细节.

改动包括:
- 优化 <T : AuthenticationDetailsProvider>.validate() 方法的 OCID 检查方式.
- 为 InlineKeyboardGroupBuilder 添加 rowButton 方法, 便于创建单行按钮.
- 新增 checkCallbackQueryIsProfileOwner 方法, 用于防范潜在的 CallbackQuery 欺诈攻击.
- callbackCache 将缩短缓存时间到 10 分钟.
- 新增 jsonObjectOf() 方法, 可简单地创建 JsonObject 对象.
This commit is contained in:
LamGC 2022-04-21 20:47:08 +08:00
parent 9d530303dc
commit a7b40cd100
Signed by: LamGC
GPG Key ID: 6C5AE2A913941E1D

View File

@ -1,3 +1,5 @@
@file:Suppress("unused")
package net.lamgc.scext.oraclemanager package net.lamgc.scext.oraclemanager
import com.google.common.base.Strings import com.google.common.base.Strings
@ -41,9 +43,9 @@ import java.security.interfaces.RSAPrivateCrtKey
import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.RSAPublicKeySpec import java.security.spec.RSAPublicKeySpec
import java.util.* import java.util.*
import java.util.concurrent.ThreadLocalRandom
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.atomic.AtomicReference
import java.util.function.Predicate
import java.util.regex.Pattern import java.util.regex.Pattern
fun BaseAbilityBot.getFileUrl(fileId: String, apiServer: String = "https://api.telegram.org"): String { fun BaseAbilityBot.getFileUrl(fileId: String, apiServer: String = "https://api.telegram.org"): String {
@ -56,9 +58,10 @@ fun <T : HttpResponse> T.isSuccess(): Boolean = code == 200
fun <T : HttpResponse> T.hasContent(): Boolean = this is HttpEntityContainer fun <T : HttpResponse> T.hasContent(): Boolean = this is HttpEntityContainer
fun <T : AuthenticationDetailsProvider> T.validate(): Boolean { fun <T : AuthenticationDetailsProvider> T.validate(): Boolean {
return !Strings.isNullOrEmpty(userId) && return OCID.isValid(userId) &&
!Strings.isNullOrEmpty(tenantId) && OCID.isValid(tenantId) &&
!Strings.isNullOrEmpty(fingerprint) !Strings.isNullOrEmpty(fingerprint) &&
fingerprintCheckPattern.matcher(fingerprint).matches()
} }
private val fingerprintCheckPattern = Pattern.compile("^[\\da-zA-Z]{2}(:[\\da-zA-Z]{2}){15}\$") private val fingerprintCheckPattern = Pattern.compile("^[\\da-zA-Z]{2}(:[\\da-zA-Z]{2}){15}\$")
@ -174,6 +177,10 @@ class InlineKeyboardGroupBuilder {
builder.keyboardRow(row) builder.keyboardRow(row)
} }
fun rowButton(apply: InlineKeyboardButtonBuilder.() -> Unit): InlineKeyboardGroupBuilder {
return newRow().addButton(apply).then()
}
fun build(): InlineKeyboardMarkup = builder.build() fun build(): InlineKeyboardMarkup = builder.build()
} }
@ -240,8 +247,14 @@ fun callbackQueryAt(actionName: String): (Update) -> Boolean {
} }
} }
fun checkCallbackQueryIsProfileOwner(): (Update) -> Boolean = { upd ->
upd.hasCallbackQuery() && upd.callbackQuery.callbackDataOrNull != null &&
upd.callbackQuery.from.id == getProfileByCallback(upd.callbackQuery.callbackData).telegramUserId
}
private val callbackCache: Cache<String, InlineKeyboardCallback> = CacheBuilder.newBuilder() private val callbackCache: Cache<String, InlineKeyboardCallback> = CacheBuilder.newBuilder()
.expireAfterAccess(30, TimeUnit.MINUTES) .expireAfterAccess(10, TimeUnit.MINUTES)
.softValues()
.build() .build()
fun InlineKeyboardButtonBuilder.callbackData(callback: InlineKeyboardCallback): InlineKeyboardButtonBuilder { fun InlineKeyboardButtonBuilder.callbackData(callback: InlineKeyboardCallback): InlineKeyboardButtonBuilder {
@ -269,13 +282,11 @@ fun createPromptKeyboard(
noMsg: String = "取消" noMsg: String = "取消"
): InlineKeyboardMarkup { ): InlineKeyboardMarkup {
return InlineKeyboardGroupBuilder() return InlineKeyboardGroupBuilder()
.newRow() .rowButton {
.addButton {
text(yesMsg) text(yesMsg)
callbackData(yesCallback) callbackData(yesCallback)
} }
.newRow() .rowButton {
.addButton {
text(noMsg) text(noMsg)
callbackData(noCallback) callbackData(noCallback)
} }
@ -356,13 +367,21 @@ fun Random.randomString(length: Int): String {
return builder.toString() return builder.toString()
} }
fun callbackQueryOf(action: String, block: (BaseAbilityBot, Update) -> Unit): Reply = fun callbackQueryOf(
Reply.of(block, callbackQueryAt(action)) action: String,
checkProfileOwner: Boolean = false,
const val JSON_FIELD_PROFILE = "profile" block: (BaseAbilityBot, Update) -> Unit
): Reply {
return Reply.of(block, mutableListOf<Predicate<Update>?>().apply {
add(callbackQueryAt(action))
if (checkProfileOwner) {
add(checkCallbackQueryIsProfileOwner())
}
}.toList())
}
fun getProfileByCallback(callback: InlineKeyboardCallback): OracleAccountProfile { fun getProfileByCallback(callback: InlineKeyboardCallback): OracleAccountProfile {
return OracleAccountProfile.fromJson(callback.extraData[JSON_FIELD_PROFILE].asJsonObject) return OracleAccountProfile.fromJson(callback.extraData[JsonFields.AccountProfile].asJsonObject)
} }
class JsonObjectBuilder(private val jsonObject: JsonObject) { class JsonObjectBuilder(private val jsonObject: JsonObject) {
@ -392,8 +411,7 @@ class JsonObjectBuilder(private val jsonObject: JsonObject) {
} }
} }
fun jsonObjectOf(block: JsonObjectBuilder.() -> Unit): JsonObject { fun jsonObjectOf(jsonObject: JsonObject = JsonObject(), block: JsonObjectBuilder.() -> Unit): JsonObject {
val jsonObject = JsonObject()
JsonObjectBuilder(jsonObject).block() JsonObjectBuilder(jsonObject).block()
return jsonObject return jsonObject
} }