Auth fixed
This commit is contained in:
-2126
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@ class SyncCommand (
|
||||
jwt = client.getToken(),
|
||||
)
|
||||
val res = client.sendRequest(initialRequest)
|
||||
print(res)
|
||||
|
||||
if (res == null) {
|
||||
return ("[!] Сервер недоступен. Попробуйте позже.")
|
||||
|
||||
@@ -146,17 +146,12 @@ class NetworkManager(
|
||||
}
|
||||
|
||||
fun setToken(newToken: String) {
|
||||
logger.warn {"New token set"}
|
||||
token = newToken
|
||||
}
|
||||
|
||||
fun getToken(): String {
|
||||
logger.warn {"token used"}
|
||||
return token
|
||||
}
|
||||
fun getToken(): String = token
|
||||
|
||||
fun clearToken() {
|
||||
logger.warn {"clear token"}
|
||||
token = "no token"
|
||||
}
|
||||
|
||||
|
||||
@@ -9,5 +9,5 @@ interface Command {
|
||||
val args: List<CommandArgType>
|
||||
val type: CommandType
|
||||
|
||||
fun execute(args: List<String>, humanBeing: HumanBeing? = null): Response
|
||||
fun execute(args: List<String>, humanBeing: HumanBeing? = null, userId: String = ""): Response
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ fun registerServerCommands(
|
||||
AddCommand(manager, repository),
|
||||
UpdateCommand(manager, repository),
|
||||
RemoveByIdCommand(manager, repository),
|
||||
ClearCommand(manager),
|
||||
ClearCommand(manager, repository),
|
||||
AddIfMaxCommand(manager, repository),
|
||||
AddIfMinCommand(manager, repository),
|
||||
HistoryCommand(invoker),
|
||||
@@ -75,7 +75,6 @@ fun registerServerCommands(
|
||||
SumOfMinutesCommand(manager),
|
||||
MinByNameCommand(manager),
|
||||
PrintDescendingMinutesCommand(manager),
|
||||
ExitCommand()
|
||||
).forEach { invoker.register(it)}
|
||||
logger.info{ " [startup] Registered commands: ${invoker.getCommands().keys}" }
|
||||
}
|
||||
|
||||
@@ -13,30 +13,38 @@ import java.time.ZonedDateTime
|
||||
class JWTManager (
|
||||
private val jwtSecret: String = "random",
|
||||
private val issuer: String = "aklivtsov.tech",
|
||||
private val subject: String = "f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3"
|
||||
) {
|
||||
private val signer: Signer = HMACSigner.newSHA256Signer(jwtSecret)
|
||||
private val verifier: Verifier = HMACVerifier.newVerifier(jwtSecret)
|
||||
|
||||
fun createJWT(expiredAfterMinutes: Long): String {
|
||||
fun createJWT(expiredAfterMinutes: Long, userId: String): String {
|
||||
val jwt: JWT = JWT()
|
||||
.setIssuer(issuer)
|
||||
.setIssuedAt(ZonedDateTime.now(ZoneOffset.UTC))
|
||||
.setSubject(subject)
|
||||
.setSubject(userId)
|
||||
.setExpiration(ZonedDateTime.now(ZoneOffset.UTC).plusMinutes(expiredAfterMinutes))
|
||||
|
||||
return JWT.getEncoder().encode(jwt, signer)
|
||||
}
|
||||
|
||||
fun validateJWT(jwt: String): Boolean {
|
||||
val decoded: JWT
|
||||
try {
|
||||
decoded = JWT.getDecoder().decode(jwt, verifier)
|
||||
} catch (e: JWTExpiredException) {
|
||||
return false
|
||||
} catch (e: JWTUnavailableForProcessingException) {
|
||||
return false
|
||||
fun getUserId(jwt: String): String? {
|
||||
return try {
|
||||
JWT.getDecoder().decode(jwt, verifier).subject
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun validateJWT(jwt: String, userId: String): Boolean {
|
||||
return try {
|
||||
JWT.getDecoder().decode(jwt, verifier)
|
||||
true
|
||||
} catch (e: JWTExpiredException) {
|
||||
false
|
||||
} catch (e: JWTUnavailableForProcessingException) {
|
||||
false
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
return decoded.subject == subject
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import models.HumanBeing
|
||||
import network.Response
|
||||
import java.util.UUID
|
||||
|
||||
@Serializable
|
||||
class AddCommand(
|
||||
@@ -17,13 +18,14 @@ class AddCommand(
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
override val type = CommandType.MODEL
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?, userId: String): Response {
|
||||
humanBeing ?: return Response(false, "[Ошибка] Объект HumanBeing не передан.")
|
||||
humanBeing.id = repository.write(humanBeing)
|
||||
humanBeing.id ?: return Response(false, "Не удалось добавить элемент")
|
||||
val secured = humanBeing.copy(ownerId = UUID.fromString(userId))
|
||||
secured.id = repository.write(secured)
|
||||
secured.id ?: return Response(false, "Не удалось добавить элемент")
|
||||
|
||||
return if (manager.add(humanBeing))
|
||||
Response(true, "Элемент добавлен: ${humanBeing.name} [${humanBeing.id}]")
|
||||
return if (manager.add(secured))
|
||||
Response(true, "Элемент добавлен: ${secured.name} [${secured.id}]")
|
||||
|
||||
else
|
||||
Response(false, "Элемент уже существует в коллекции.")
|
||||
|
||||
@@ -6,6 +6,7 @@ import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import models.HumanBeing
|
||||
import network.Response
|
||||
import java.util.UUID
|
||||
|
||||
@Serializable
|
||||
class AddIfMaxCommand(
|
||||
@@ -17,13 +18,14 @@ class AddIfMaxCommand(
|
||||
override val type = CommandType.MODEL
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?, userId: String): Response {
|
||||
humanBeing ?: return Response(false, "[Ошибка] Объект HumanBeing не передан.")
|
||||
val secured = humanBeing.copy(ownerId = UUID.fromString(userId))
|
||||
val max = manager.getMax()
|
||||
return if (max == null || humanBeing > max) {
|
||||
humanBeing.id = repository.write(humanBeing)
|
||||
humanBeing.id ?: return Response(false, "Не удалось добавить элемент")
|
||||
manager.add(humanBeing)
|
||||
return if (max == null || secured > max) {
|
||||
secured.id = repository.write(secured)
|
||||
secured.id ?: return Response(false, "Не удалось добавить элемент")
|
||||
manager.add(secured)
|
||||
Response(true, "Элемент добавлен (превышает максимум).")
|
||||
} else {
|
||||
Response(false, "Элемент не добавлен: не превышает текущий максимум (${max.name}).")
|
||||
|
||||
@@ -6,6 +6,7 @@ import models.HumanBeing
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Contextual
|
||||
import network.Response
|
||||
import java.util.UUID
|
||||
|
||||
@Serializable
|
||||
class AddIfMinCommand(
|
||||
@@ -17,13 +18,14 @@ class AddIfMinCommand(
|
||||
override val type = CommandType.MODEL
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?, userId: String): Response {
|
||||
humanBeing ?: return Response(false, "[Ошибка] Объект HumanBeing не передан.")
|
||||
val secured = humanBeing.copy(ownerId = UUID.fromString(userId))
|
||||
val min = manager.getMin()
|
||||
return if (min == null || humanBeing < min) {
|
||||
humanBeing.id = repository.write(humanBeing)
|
||||
humanBeing.id ?: return Response(false, "Не удалось добавить элемент")
|
||||
manager.add(humanBeing)
|
||||
return if (min == null || secured < min) {
|
||||
secured.id = repository.write(secured)
|
||||
secured.id ?: return Response(false, "Не удалось добавить элемент")
|
||||
manager.add(secured)
|
||||
Response(true, "Элемент добавлен (меньше минимума).")
|
||||
} else {
|
||||
Response(false, "Элемент не добавлен: не меньше текущего минимума (${min.name}).")
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
package commands
|
||||
|
||||
import collection.CollectionManager
|
||||
import database.Repository
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import models.HumanBeing
|
||||
import network.Response
|
||||
import java.util.UUID
|
||||
|
||||
@Serializable
|
||||
class ClearCommand(@Contextual private val manager: CollectionManager) : Command {
|
||||
class ClearCommand(
|
||||
@Contextual private val manager: CollectionManager,
|
||||
@Contextual private var repository: Repository
|
||||
) : Command {
|
||||
override val name = "clear"
|
||||
override val description = "очистить коллекцию"
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
override val type = CommandType.SIMPLE
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||
manager.clear()
|
||||
return Response(true, "Коллекция очищена.")
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?, userId: String): Response {
|
||||
if (userId.isBlank()) return Response(false, "Не удалось определить пользователя.")
|
||||
val ownerId = UUID.fromString(userId)
|
||||
|
||||
val deletedCount = repository.deleteByOwner(ownerId)
|
||||
val toRemove = manager.getAll().filter { it.ownerId == ownerId }
|
||||
toRemove.forEach { manager.removeById(it.id!!) }
|
||||
|
||||
return Response(true, "Удалено объектов: $deletedCount.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package commands
|
||||
|
||||
import models.HumanBeing
|
||||
import network.Response
|
||||
|
||||
/**
|
||||
* Базовый интерфейс команды.
|
||||
*
|
||||
* Теперь execute() возвращает String — результат для отправки клиенту.
|
||||
* Зависимость от IOManager полностью убрана из команд.
|
||||
*/
|
||||
interface Command {
|
||||
val name: String
|
||||
val description: String
|
||||
val type : CommandType
|
||||
val args : List<CommandArgType>
|
||||
|
||||
/**
|
||||
* @param args строковые аргументы (UUID и т.п.)
|
||||
* @param humanBeing объект из запроса — только для add/update/add_if_max/add_if_min
|
||||
* @return строка-результат для клиента
|
||||
*/
|
||||
fun execute(args: List<String>, humanBeing: HumanBeing? = null): Response
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package commands
|
||||
|
||||
/**
|
||||
* Завершает программу без сохранения.
|
||||
*/
|
||||
import kotlinx.serialization.Serializable
|
||||
import models.HumanBeing
|
||||
import network.Response
|
||||
|
||||
@Serializable
|
||||
class ExitCommand() : Command {
|
||||
override val name = "exit"
|
||||
override val description = "завершить программу"
|
||||
override val type = CommandType.SIMPLE
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response = Response(true, "")
|
||||
}
|
||||
@@ -13,7 +13,7 @@ class HistoryCommand(@Contextual private val invoker: CommandInvoker) : Command
|
||||
override val type = CommandType.SIMPLE
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?, userId: String): Response {
|
||||
val history = invoker.getHistory()
|
||||
if (history.isEmpty()) return Response(true, "История пуста.")
|
||||
return Response(true, history.mapIndexed { i, cmd -> " ${i + 1}. $cmd" }.joinToString("\n"))
|
||||
|
||||
@@ -13,6 +13,6 @@ class InfoCommand(@Contextual private val manager: CollectionManager) : Command
|
||||
override val type = CommandType.SIMPLE
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response =
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?, userId: String): Response =
|
||||
Response(true, manager.getInfo())
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ class MinByNameCommand(@Contextual private val manager: CollectionManager) : Com
|
||||
override val type = CommandType.SIMPLE
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response =
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?, userId: String): Response =
|
||||
Response(true, manager.minByName()?.toString() ?: "Коллекция пуста.")
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class PrintDescendingMinutesCommand(@Contextual private val manager: CollectionM
|
||||
override val type = CommandType.SIMPLE
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?, userId: String): Response {
|
||||
val values = manager.getMinutesOfWaitingDescending()
|
||||
return if (values.isEmpty()) Response(true, "Коллекция пуста.")
|
||||
else Response(true, values.joinToString("\n"))
|
||||
|
||||
@@ -18,7 +18,7 @@ class RemoveByIdCommand(
|
||||
override val type = CommandType.SIMPLE
|
||||
override val args = listOf(CommandArgType.STRING)
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?, userId: String): Response {
|
||||
if (args.isEmpty()) return Response(false, "[Ошибка] Укажите id. Пример: remove_by_id <uuid>")
|
||||
|
||||
val id = try {
|
||||
@@ -27,6 +27,12 @@ class RemoveByIdCommand(
|
||||
return Response(false, "[Ошибка] Некорректный UUID: '${args[0]}'")
|
||||
}
|
||||
|
||||
val existing = manager.getById(id)
|
||||
?: return Response(false, "Элемент не найден.")
|
||||
|
||||
if (existing.ownerId.toString() != userId)
|
||||
return Response(false, "Нет прав: объект принадлежит другому пользователю.")
|
||||
|
||||
if (repository.delete(id)) {
|
||||
manager.removeById(id)
|
||||
return Response(true, "Элемент удалён.")
|
||||
|
||||
@@ -13,7 +13,7 @@ class ShowCommand(@Contextual private val manager: CollectionManager) : Command
|
||||
override val type = CommandType.SIMPLE
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?, userId: String): Response {
|
||||
if (manager.isEmpty()) return Response(true, "Коллекция пуста.")
|
||||
return Response(true, manager.getAll().joinToString("\n") { it.toString() })
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ class SumOfMinutesCommand(@Contextual private val manager: CollectionManager) :
|
||||
override val type = CommandType.SIMPLE
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response =
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?, userId: String): Response =
|
||||
Response(true, "Сумма minutesOfWaiting: ${manager.sumOfMinutesOfWaiting()}")
|
||||
}
|
||||
|
||||
@@ -16,12 +16,12 @@ class SyncCommand(
|
||||
override val type = CommandType.SIMPLE
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?, userId: String): Response {
|
||||
|
||||
val commands = mutableMapOf<String, Command>()
|
||||
|
||||
for (command in invoker.getCommands().values) {
|
||||
if (command.name == "sync" || command.name == "exit") {
|
||||
if (command.name == "sync") {
|
||||
continue
|
||||
}
|
||||
commands[command.name] = command
|
||||
|
||||
@@ -18,7 +18,7 @@ class UpdateCommand(
|
||||
override val type = CommandType.MODEL
|
||||
override val args = listOf(CommandArgType.STRING)
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?, userId: String): Response {
|
||||
if (args.isEmpty()) return Response(false, "[Ошибка] Укажите id. Пример: update <UUID>")
|
||||
humanBeing ?: return Response(false, "[Ошибка] Объект HumanBeing не передан.")
|
||||
|
||||
@@ -28,6 +28,12 @@ class UpdateCommand(
|
||||
return Response(false, "[Ошибка] Некорректный UUID: '${args[0]}'")
|
||||
}
|
||||
|
||||
val existing = manager.getById(id)
|
||||
?: return Response(false, "Элемент не найден.")
|
||||
|
||||
if (existing.ownerId.toString() != userId)
|
||||
return Response(false, "Нет прав: объект принадлежит другому пользователю.")
|
||||
|
||||
if (repository.update(id, humanBeing)) {
|
||||
manager.update(id, humanBeing)
|
||||
return Response(true, "Элемент обновлён.")
|
||||
|
||||
@@ -142,6 +142,7 @@ class Repository(
|
||||
return allGood
|
||||
}
|
||||
|
||||
// nuke 'em
|
||||
fun delete(id: UUID): Boolean {
|
||||
var allGood = true
|
||||
try {
|
||||
@@ -154,45 +155,60 @@ class Repository(
|
||||
return allGood
|
||||
}
|
||||
|
||||
fun login (username: String, password: String): Pair<String, String>? {
|
||||
val newToken = jwtManager.createJWT(60)
|
||||
fun login(username: String, password: String): Pair<String, String>? {
|
||||
var id: String? = null
|
||||
try {
|
||||
transaction {
|
||||
id = UserTable
|
||||
.select(UserTable.id)
|
||||
.where{ (UserTable.username eq username) and (UserTable.password eq password) }
|
||||
.single()[UserTable.id].toString()
|
||||
|
||||
UserTable
|
||||
.update({ UserTable.id eq UUID.fromString(id) }) {
|
||||
it[UserTable.username] = newToken
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
return null
|
||||
transaction {
|
||||
id = UserTable.select(UserTable.id)
|
||||
.where { (UserTable.username eq username) and (UserTable.password eq password) }
|
||||
.singleOrNull()
|
||||
?.get(UserTable.id)?.toString()
|
||||
}
|
||||
return if (id != null) Pair(newToken, id) else null
|
||||
if (id == null) return null
|
||||
val newToken = jwtManager.createJWT(60, id!!)
|
||||
transaction {
|
||||
UserTable.update({ UserTable.id eq UUID.fromString(id) }) {
|
||||
it[UserTable.token] = newToken
|
||||
}
|
||||
}
|
||||
return Pair(newToken, id!!)
|
||||
}
|
||||
|
||||
fun register(username: String, password: String): Pair<String, String>? {
|
||||
val newToken = jwtManager.createJWT(60)
|
||||
var id: String? = null
|
||||
transaction {
|
||||
val check = UserTable
|
||||
.select(UserTable.username)
|
||||
.where { (UserTable.username eq username) or (UserTable.password eq password) }
|
||||
val check = UserTable.select(UserTable.username)
|
||||
.where { UserTable.username eq username }
|
||||
.singleOrNull()
|
||||
|
||||
if (check == null) {
|
||||
id = UserTable
|
||||
.insertAndGetId {
|
||||
it[UserTable.username] = username
|
||||
it[UserTable.password] = password
|
||||
it[UserTable.token] = newToken
|
||||
}.value.toString()
|
||||
id = UserTable.insertAndGetId {
|
||||
it[UserTable.username] = username
|
||||
it[UserTable.password] = password
|
||||
}.value.toString()
|
||||
}
|
||||
}
|
||||
return if (id != null) Pair(newToken, id) else null
|
||||
if (id == null) return null
|
||||
val newToken = jwtManager.createJWT(60, id!!)
|
||||
transaction {
|
||||
UserTable.update({ UserTable.id eq UUID.fromString(id) }) {
|
||||
it[UserTable.token] = newToken
|
||||
}
|
||||
}
|
||||
return Pair(newToken, id!!)
|
||||
}
|
||||
|
||||
fun deleteByOwner(ownerId: UUID): Int {
|
||||
var deletedCount = 0
|
||||
transaction {
|
||||
val humanIds = HumanBeingTable
|
||||
.select(HumanBeingTable.id, HumanBeingTable.coordinates, HumanBeingTable.car)
|
||||
.where { HumanBeingTable.owner_id eq ownerId }
|
||||
.toList()
|
||||
val coordIds = humanIds.map { it[HumanBeingTable.coordinates] }
|
||||
val carIds = humanIds.map { it[HumanBeingTable.car] }
|
||||
deletedCount = HumanBeingTable.deleteWhere { HumanBeingTable.owner_id eq ownerId }
|
||||
CoordinatesTable.deleteWhere { CoordinatesTable.id inList coordIds }
|
||||
CarTable.deleteWhere { CarTable.id inList carIds }
|
||||
}
|
||||
return deletedCount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,18 +30,19 @@ class CommandInvoker(
|
||||
val command = commands[request.commandName]
|
||||
?: return Response(false, "Неизвестная команда: '${request.commandName}'")
|
||||
|
||||
if (request.jwt == "no token") {
|
||||
if (request.jwt == "no token")
|
||||
return Response(false, "Нет токена. Войдите или зарегистрируйтесь")
|
||||
}
|
||||
val check = jwtManager.validateJWT(request.jwt)
|
||||
if (!check) {
|
||||
|
||||
if (!jwtManager.validateJWT(request.jwt, request.jwt))
|
||||
return Response(false, "Время истекло. Выполните вход заново.")
|
||||
}
|
||||
|
||||
val userId = jwtManager.getUserId(request.jwt)
|
||||
?: return Response(false, "Не удалось определить пользователя")
|
||||
|
||||
addToHistory(request.commandName)
|
||||
|
||||
return try {
|
||||
command.execute(request.args, request.humanBeing)
|
||||
command.execute(request.args, request.humanBeing, userId)
|
||||
} catch (e: Exception) {
|
||||
Response(false, "[Ошибка выполнения] ${e.message}")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user