mirror of
https://github.com/arcan1s/ffxivbis.git
synced 2025-04-24 17:27:17 +00:00
use strict validator on input strings via api (#15)
It has been reported that that views are vulnerable for XSS because of missing escaping (or validation). Instead of playing with conversion from/to escaped/unescaped strings lets just forbid characters via api This commit includes migration for postgres, sqlite migration is still missing which will make it impossible to load pages for those parties. This commit also includes several fixes: * The issue when empty party could not be loaded * The issue when link biis is not appllied after editing * The issue when incorrect bis link has been saved * The issue when empty password could be applied via api * The issue when error message is not displayed at the index page This commit also updates dependencies
This commit is contained in:
parent
118d8faf6b
commit
0e8b95d0dd
@ -1,26 +1,26 @@
|
||||
val AkkaVersion = "2.6.18"
|
||||
val AkkaHttpVersion = "10.2.7"
|
||||
val ScalaTestVersion = "3.2.10"
|
||||
val AkkaVersion = "2.6.19"
|
||||
val AkkaHttpVersion = "10.2.9"
|
||||
val ScalaTestVersion = "3.2.12"
|
||||
val SlickVersion = "3.3.3"
|
||||
|
||||
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.10"
|
||||
libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.4"
|
||||
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.11"
|
||||
libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5"
|
||||
|
||||
libraryDependencies += "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion
|
||||
libraryDependencies += "com.typesafe.akka" %% "akka-http-spray-json" % AkkaHttpVersion
|
||||
libraryDependencies += "com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion
|
||||
libraryDependencies += "com.typesafe.akka" %% "akka-stream" % AkkaVersion
|
||||
libraryDependencies += "com.github.swagger-akka-http" %% "swagger-akka-http" % "2.6.0"
|
||||
libraryDependencies += "com.github.swagger-akka-http" %% "swagger-akka-http" % "2.7.0"
|
||||
libraryDependencies += "jakarta.platform" % "jakarta.jakartaee-web-api" % "9.1.0"
|
||||
libraryDependencies += "ch.megard" %% "akka-http-cors" % "1.1.2"
|
||||
libraryDependencies += "ch.megard" %% "akka-http-cors" % "1.1.3"
|
||||
|
||||
libraryDependencies += "io.spray" %% "spray-json" % "1.3.6"
|
||||
|
||||
libraryDependencies += "org.playframework.anorm" %% "anorm" % "2.6.10"
|
||||
libraryDependencies += "com.zaxxer" % "HikariCP" % "5.0.1" exclude("org.slf4j", "slf4j-api")
|
||||
libraryDependencies += "org.flywaydb" % "flyway-core" % "8.4.1"
|
||||
libraryDependencies += "org.flywaydb" % "flyway-core" % "8.5.12"
|
||||
libraryDependencies += "org.xerial" % "sqlite-jdbc" % "3.36.0.3"
|
||||
libraryDependencies += "org.postgresql" % "postgresql" % "42.3.1"
|
||||
libraryDependencies += "org.postgresql" % "postgresql" % "42.3.6"
|
||||
|
||||
libraryDependencies += "org.mindrot" % "jbcrypt" % "0.4"
|
||||
libraryDependencies += "com.google.guava" % "guava" % "31.0.1-jre"
|
||||
|
@ -0,0 +1,3 @@
|
||||
update parties set party_alias = regexp_replace(party_alias, '[^A-Za-z0-9!@#$%^&*()\-_=+;:'',./? ]', '', 'g');
|
||||
update players set nick = regexp_replace(nick, '[^A-Za-z0-9!@#$%^&*()\-_=+;:'',./? ]', '', 'g');
|
||||
update users set username = regexp_replace(username, '[^A-Za-z0-9!@#$%^&*()\-_=+;:'',./? ]', '', 'g');
|
@ -9,9 +9,9 @@
|
||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.3/font/bootstrap-icons.css" type="text/css">
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.css" type="text/css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.20.2/dist/bootstrap-table.min.css" type="text/css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" type="text/css">
|
||||
@ -157,11 +157,11 @@
|
||||
<script src="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.js"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/bootstrap-table.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/bootstrap-table@1.19.1/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/bootstrap-table@1.19.1/dist/extensions/resizable/bootstrap-table-resizable.js"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/extensions/resizable/bootstrap-table-resizable.js"></script>
|
||||
|
||||
<script src="/static/utils.js"></script>
|
||||
<script src="/static/load.js"></script>
|
||||
|
@ -87,6 +87,7 @@
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||
|
||||
<script src="/static/utils.js"></script>
|
||||
<script src="/static/load.js"></script>
|
||||
|
||||
<script>
|
||||
|
@ -9,9 +9,9 @@
|
||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.3/font/bootstrap-icons.css" type="text/css">
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.css" type="text/css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.20.2/dist/bootstrap-table.min.css" type="text/css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" type="text/css">
|
||||
@ -174,11 +174,11 @@
|
||||
<script src="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.js"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/bootstrap-table.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/bootstrap-table@1.19.1/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/bootstrap-table@1.19.1/dist/extensions/resizable/bootstrap-table-resizable.js"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/extensions/resizable/bootstrap-table-resizable.js"></script>
|
||||
|
||||
<script src="/static/utils.js"></script>
|
||||
<script src="/static/load.js"></script>
|
||||
|
@ -9,9 +9,9 @@
|
||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.3/font/bootstrap-icons.css" type="text/css">
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.css" type="text/css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.20.2/dist/bootstrap-table.min.css" type="text/css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" type="text/css">
|
||||
@ -147,11 +147,11 @@
|
||||
<script src="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.js"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/bootstrap-table.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/bootstrap-table@1.19.1/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/bootstrap-table@1.19.1/dist/extensions/resizable/bootstrap-table-resizable.js"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/extensions/resizable/bootstrap-table-resizable.js"></script>
|
||||
|
||||
<script src="/static/utils.js"></script>
|
||||
<script src="/static/load.js"></script>
|
||||
|
@ -9,9 +9,9 @@
|
||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" type="text/css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.3/font/bootstrap-icons.css" type="text/css">
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.css" type="text/css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.20.2/dist/bootstrap-table.min.css" type="text/css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" type="text/css">
|
||||
@ -141,11 +141,11 @@
|
||||
<script src="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.19.1/dist/bootstrap-table.min.js"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/bootstrap-table.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/bootstrap-table@1.19.1/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/bootstrap-table@1.19.1/dist/extensions/resizable/bootstrap-table-resizable.js"></script>
|
||||
<script src="https://unpkg.com/bootstrap-table@1.20.2/dist/extensions/resizable/bootstrap-table-resizable.js"></script>
|
||||
|
||||
<script src="/static/utils.js"></script>
|
||||
<script src="/static/load.js"></script>
|
||||
|
@ -0,0 +1,16 @@
|
||||
package me.arcanis.ffxivbis.http
|
||||
|
||||
import scala.collection.immutable.HashSet
|
||||
|
||||
trait ValidatorHelper {
|
||||
|
||||
def isValidString(string: String): Boolean = string.nonEmpty && string.forall(isValidSymbol)
|
||||
|
||||
def isValidSymbol(char: Char): Boolean =
|
||||
char.isLetterOrDigit || ValidatorHelper.VALID_CHARACTERS.contains(char)
|
||||
}
|
||||
|
||||
object ValidatorHelper {
|
||||
|
||||
final val VALID_CHARACTERS = HashSet.from("!@#$%^&*()-_=+;:',./? ")
|
||||
}
|
@ -49,7 +49,12 @@ class BiSEndpoint(
|
||||
summary = "create best in slot",
|
||||
description = "Create the best in slot set",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
),
|
||||
requestBody = new RequestBody(
|
||||
description = "player best in slot description",
|
||||
@ -105,7 +110,12 @@ class BiSEndpoint(
|
||||
summary = "get best in slot",
|
||||
description = "Return the best in slot items",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
new Parameter(
|
||||
name = "nick",
|
||||
in = ParameterIn.QUERY,
|
||||
@ -167,7 +177,12 @@ class BiSEndpoint(
|
||||
summary = "modify best in slot",
|
||||
description = "Add or remove an item from the best in slot",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
),
|
||||
requestBody = new RequestBody(
|
||||
description = "action and piece description",
|
||||
|
@ -46,7 +46,12 @@ class LootEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
summary = "get loot list",
|
||||
description = "Return the looted items",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
new Parameter(
|
||||
name = "nick",
|
||||
in = ParameterIn.QUERY,
|
||||
@ -107,7 +112,12 @@ class LootEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
summary = "modify loot list",
|
||||
description = "Add or remove an item from the loot list",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
),
|
||||
requestBody = new RequestBody(
|
||||
description = "action and piece description",
|
||||
@ -164,7 +174,12 @@ class LootEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
summary = "suggest loot",
|
||||
description = "Suggest loot piece to party",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
),
|
||||
requestBody = new RequestBody(
|
||||
description = "piece description",
|
||||
|
@ -49,7 +49,12 @@ class PartyEndpoint(
|
||||
summary = "get party description",
|
||||
description = "Return the party description",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
),
|
||||
responses = Array(
|
||||
new ApiResponse(
|
||||
@ -96,7 +101,12 @@ class PartyEndpoint(
|
||||
summary = "modify party description",
|
||||
description = "Edit party description",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
),
|
||||
requestBody = new RequestBody(
|
||||
description = "new party description",
|
||||
|
@ -50,7 +50,12 @@ class PlayerEndpoint(
|
||||
summary = "get party",
|
||||
description = "Return the players who belong to the party",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
new Parameter(
|
||||
name = "nick",
|
||||
in = ParameterIn.QUERY,
|
||||
@ -111,7 +116,12 @@ class PlayerEndpoint(
|
||||
summary = "get party statistics",
|
||||
description = "Return the party statistics",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
new Parameter(
|
||||
name = "nick",
|
||||
in = ParameterIn.QUERY,
|
||||
@ -172,7 +182,12 @@ class PlayerEndpoint(
|
||||
summary = "modify party",
|
||||
description = "Add or remove a player from party list",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
),
|
||||
requestBody = new RequestBody(
|
||||
description = "player description",
|
||||
|
@ -96,7 +96,12 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
summary = "create new user",
|
||||
description = "Add an user to the specified party",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
),
|
||||
requestBody = new RequestBody(
|
||||
description = "user description",
|
||||
@ -151,7 +156,12 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
summary = "remove user",
|
||||
description = "Remove an user from the specified party",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
new Parameter(name = "username", in = ParameterIn.PATH, description = "username to remove", example = "siuan"),
|
||||
),
|
||||
responses = Array(
|
||||
@ -195,7 +205,12 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
summary = "get users",
|
||||
description = "Return the list of users belong to party",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
),
|
||||
responses = Array(
|
||||
new ApiResponse(
|
||||
@ -246,7 +261,12 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
||||
summary = "get current user",
|
||||
description = "Return the current user descriptor",
|
||||
parameters = Array(
|
||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
||||
new Parameter(
|
||||
name = "partyId",
|
||||
in = ParameterIn.PATH,
|
||||
description = "unique party ID",
|
||||
example = "o3KicHQPW5b0JcOm5yI3"
|
||||
),
|
||||
),
|
||||
responses = Array(
|
||||
new ApiResponse(
|
||||
|
@ -12,9 +12,11 @@ import io.swagger.v3.oas.annotations.media.Schema
|
||||
import me.arcanis.ffxivbis.models.PartyDescription
|
||||
|
||||
case class PartyDescriptionModel(
|
||||
@Schema(description = "party id", required = true, example = "abcdefgh") partyId: String,
|
||||
@Schema(description = "party id", required = true, example = "o3KicHQPW5b0JcOm5yI3") partyId: String,
|
||||
@Schema(description = "party name") partyAlias: Option[String]
|
||||
) {
|
||||
) extends Validator {
|
||||
|
||||
require(partyAlias.forall(isValidString), stringMatchError("Party alias"))
|
||||
|
||||
def toDescription: PartyDescription = PartyDescription(partyId, partyAlias)
|
||||
}
|
||||
|
@ -10,4 +10,6 @@ package me.arcanis.ffxivbis.http.api.v1.json
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
|
||||
case class PartyIdModel(@Schema(description = "party id", required = true, example = "abcdefgh") partyId: String)
|
||||
case class PartyIdModel(
|
||||
@Schema(description = "party id", required = true, example = "o3KicHQPW5b0JcOm5yI3") partyId: String
|
||||
)
|
||||
|
@ -17,4 +17,7 @@ case class PlayerBiSLinkModel(
|
||||
example = "https://ffxiv.ariyala.com/19V5R"
|
||||
) link: String,
|
||||
@Schema(description = "player description", required = true) playerId: PlayerIdModel
|
||||
)
|
||||
) extends Validator {
|
||||
|
||||
require(isValidString(link), stringMatchError("BiS link"))
|
||||
}
|
||||
|
@ -12,10 +12,14 @@ import io.swagger.v3.oas.annotations.media.Schema
|
||||
import me.arcanis.ffxivbis.models.{Job, PlayerId}
|
||||
|
||||
case class PlayerIdModel(
|
||||
@Schema(description = "unique party ID. Required in responses", example = "abcdefgh") partyId: Option[String],
|
||||
@Schema(description = "unique party ID. Required in responses", example = "o3KicHQPW5b0JcOm5yI3") partyId: Option[
|
||||
String
|
||||
],
|
||||
@Schema(description = "job name", required = true, example = "DNC") job: String,
|
||||
@Schema(description = "player nick name", required = true, example = "Siuan Sanche") nick: String
|
||||
) {
|
||||
) extends Validator {
|
||||
|
||||
require(isValidString(nick), stringMatchError("Player name"))
|
||||
|
||||
def withPartyId(partyId: String): PlayerId =
|
||||
PlayerId(partyId, Job.withName(job), nick)
|
||||
|
@ -12,7 +12,7 @@ import io.swagger.v3.oas.annotations.media.Schema
|
||||
import me.arcanis.ffxivbis.models.PlayerIdWithCounters
|
||||
|
||||
case class PlayerIdWithCountersModel(
|
||||
@Schema(description = "unique party ID", required = true, example = "abcdefgh") partyId: String,
|
||||
@Schema(description = "unique party ID", required = true, example = "o3KicHQPW5b0JcOm5yI3") partyId: String,
|
||||
@Schema(description = "job name", required = true, example = "DNC") job: String,
|
||||
@Schema(description = "player nick name", required = true, example = "Siuan Sanche") nick: String,
|
||||
@Schema(description = "is piece required by player or not", required = true) isRequired: Boolean,
|
||||
|
@ -12,7 +12,7 @@ import io.swagger.v3.oas.annotations.media.Schema
|
||||
import me.arcanis.ffxivbis.models.{BiS, Job, Player}
|
||||
|
||||
case class PlayerModel(
|
||||
@Schema(description = "unique party ID", required = true, example = "abcdefgh") partyId: String,
|
||||
@Schema(description = "unique party ID", required = true, example = "o3KicHQPW5b0JcOm5yI3") partyId: String,
|
||||
@Schema(description = "job name", required = true, example = "DNC") job: String,
|
||||
@Schema(description = "player nick name", required = true, example = "Siuan Sanche") nick: String,
|
||||
@Schema(description = "pieces in best in slot") bis: Option[Seq[PieceModel]],
|
||||
@ -24,7 +24,10 @@ case class PlayerModel(
|
||||
`type` = "number"
|
||||
) lootCountBiS: Option[Int],
|
||||
@Schema(description = "total count of looted pieces", `type` = "number") lootCountTotal: Option[Int],
|
||||
) {
|
||||
) extends Validator {
|
||||
|
||||
require(isValidString(nick), stringMatchError("Player name"))
|
||||
require(link.forall(isValidString), stringMatchError("BiS link"))
|
||||
|
||||
def toPlayer: Player =
|
||||
Player(
|
||||
|
@ -12,23 +12,30 @@ import io.swagger.v3.oas.annotations.media.Schema
|
||||
import me.arcanis.ffxivbis.models.{Permission, User}
|
||||
|
||||
case class UserModel(
|
||||
@Schema(description = "unique party ID", required = true, example = "abcdefgh") partyId: String,
|
||||
@Schema(description = "unique party ID", required = true, example = "o3KicHQPW5b0JcOm5yI3") partyId: String,
|
||||
@Schema(description = "username to login to party", required = true, example = "siuan") username: String,
|
||||
@Schema(description = "password to login to party", required = true, example = "pa55w0rd") password: String,
|
||||
@Schema(description = "password to login to party, required for user editing", example = "pa55w0rd") password: Option[
|
||||
String
|
||||
],
|
||||
@Schema(
|
||||
description = "user permission",
|
||||
defaultValue = "get",
|
||||
`type` = "string",
|
||||
allowableValues = Array("get", "post", "admin")
|
||||
) permission: Option[Permission.Value] = None
|
||||
) {
|
||||
) extends Validator {
|
||||
|
||||
require(isValidString(username), stringMatchError("Username"))
|
||||
require(password.forall(_.nonEmpty), "Password must not be empty")
|
||||
|
||||
def toUser: User =
|
||||
User(partyId, username, password, permission.getOrElse(Permission.get))
|
||||
password.fold(throw new IllegalArgumentException("Password must noot be empty"))(
|
||||
User(partyId, username, _, permission.getOrElse(Permission.get))
|
||||
)
|
||||
}
|
||||
|
||||
object UserModel {
|
||||
|
||||
def fromUser(user: User): UserModel =
|
||||
UserModel(user.partyId, user.username, "", Some(user.permission))
|
||||
UserModel(user.partyId, user.username, None, Some(user.permission))
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
package me.arcanis.ffxivbis.http.api.v1.json
|
||||
|
||||
import me.arcanis.ffxivbis.http.ValidatorHelper
|
||||
|
||||
trait Validator extends ValidatorHelper {
|
||||
|
||||
def stringMatchError(what: String): String =
|
||||
s"$what must contain only letters or digits or one of (${ValidatorHelper.VALID_CHARACTERS.mkString(", ")})"
|
||||
}
|
@ -45,13 +45,15 @@ trait BiSHelper extends BisProviderHelper {
|
||||
timeout: Timeout,
|
||||
scheduler: Scheduler
|
||||
): Future[Unit] =
|
||||
storage.ask(RemovePiecesFromBiS(playerId, _)).flatMap { _ =>
|
||||
storage
|
||||
.ask(RemovePiecesFromBiS(playerId, _))
|
||||
.flatMap { _ =>
|
||||
downloadBiS(link, playerId.job)
|
||||
.flatMap { bis =>
|
||||
Future.traverse(bis.pieces)(addPieceBiS(playerId, _))
|
||||
}
|
||||
.map(_ => ())
|
||||
}
|
||||
.flatMap(_ => storage.ask(UpdateBiSLink(playerId, link, _)))
|
||||
|
||||
def removePieceBiS(playerId: PlayerId, piece: Piece)(implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] =
|
||||
storage.ask(RemovePieceFromBiS(playerId, piece, _))
|
||||
|
@ -27,15 +27,15 @@ trait PlayerHelper extends BisProviderHelper {
|
||||
)(implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Unit] =
|
||||
storage
|
||||
.ask(ref => AddPlayer(player, ref))
|
||||
.map { res =>
|
||||
.map { _ =>
|
||||
player.link.map(_.trim).filter(_.nonEmpty) match {
|
||||
case Some(link) =>
|
||||
downloadBiS(link, player.job)
|
||||
.map { bis =>
|
||||
bis.pieces.map(piece => storage.ask(AddPieceToBis(player.playerId, piece, _)))
|
||||
}
|
||||
.map(_ => res)
|
||||
case None => Future.successful(res)
|
||||
.flatMap(_ => storage.ask(UpdateBiSLink(player.playerId, link, _)))
|
||||
case None => Future.successful(())
|
||||
}
|
||||
}
|
||||
.flatten
|
||||
|
@ -95,6 +95,11 @@ object DatabaseMessage {
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class UpdateBiSLink(playerId: PlayerId, link: String, actorRef: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||
override val partyId: String = playerId.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class UpdateParty(partyDescription: PartyDescription, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||
override val partyId: String = partyDescription.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
|
@ -62,6 +62,10 @@ trait DatabasePartyHandler { this: Database =>
|
||||
run(profile.deletePlayer(playerId))(_ => client ! ())
|
||||
Behaviors.same
|
||||
|
||||
case UpdateBiSLink(playerId, link, client) =>
|
||||
run(profile.updateBiSLink(playerId, link))(_ => client ! ())
|
||||
Behaviors.same
|
||||
|
||||
case UpdateParty(description, client) =>
|
||||
run(profile.insertPartyDescription(description))(_ => client ! ())
|
||||
Behaviors.same
|
||||
|
@ -53,6 +53,8 @@ trait BiSProfile extends DatabaseConnection {
|
||||
def getPiecesBiSById(playerId: Long): Future[Seq[Loot]] = getPiecesBiSById(Seq(playerId))
|
||||
|
||||
def getPiecesBiSById(playerIds: Seq[Long]): Future[Seq[Loot]] =
|
||||
if (playerIds.isEmpty) Future.successful(Seq.empty)
|
||||
else
|
||||
withConnection { implicit conn =>
|
||||
SQL("""select * from bis where player_id in ({player_ids})""")
|
||||
.on("player_ids" -> playerIds)
|
||||
|
@ -59,6 +59,8 @@ trait LootProfile extends DatabaseConnection {
|
||||
def getPiecesById(playerId: Long): Future[Seq[Loot]] = getPiecesById(Seq(playerId))
|
||||
|
||||
def getPiecesById(playerIds: Seq[Long]): Future[Seq[Loot]] =
|
||||
if (playerIds.isEmpty) Future.successful(Seq.empty)
|
||||
else
|
||||
withConnection { implicit conn =>
|
||||
SQL("""select * from loot where player_id in ({player_ids})""")
|
||||
.on("player_ids" -> playerIds)
|
||||
|
@ -101,4 +101,18 @@ trait PlayersProfile extends DatabaseConnection {
|
||||
.executeUpdate()
|
||||
}
|
||||
|
||||
def updateBiSLink(playerId: PlayerId, link: String): Future[Int] =
|
||||
withConnection { implicit conn =>
|
||||
SQL("""update players
|
||||
| set bis_link = {link}
|
||||
| where party_id = {party_id} and nick = {nick} and job = {job}""".stripMargin)
|
||||
.on(
|
||||
"link" -> link,
|
||||
"party_id" -> playerId.partyId,
|
||||
"nick" -> playerId.nick,
|
||||
"job" -> playerId.job.toString
|
||||
)
|
||||
.executeUpdate()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ class UserEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRoute
|
||||
|
||||
"create a party" in {
|
||||
val uri = Uri(s"/party")
|
||||
val entity = UserModel.fromUser(Fixtures.userAdmin).copy(password = Fixtures.userPassword)
|
||||
val entity = UserModel.fromUser(Fixtures.userAdmin).copy(password = Some(Fixtures.userPassword))
|
||||
|
||||
Post(uri, entity) ~> route ~> check {
|
||||
status shouldEqual StatusCodes.OK
|
||||
@ -57,7 +57,7 @@ class UserEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRoute
|
||||
}
|
||||
|
||||
"add user" in {
|
||||
val entity = UserModel.fromUser(Fixtures.userGet).copy(partyId = partyId, password = Fixtures.userPassword2)
|
||||
val entity = UserModel.fromUser(Fixtures.userGet).copy(partyId = partyId, password = Some(Fixtures.userPassword2))
|
||||
|
||||
Post(endpoint, entity).withHeaders(auth) ~> route ~> check {
|
||||
status shouldEqual StatusCodes.Accepted
|
||||
|
@ -66,6 +66,18 @@ class DatabasePartyHandlerTest extends ScalaTestWithActorTestKit(Settings.withRa
|
||||
Compare.seqEquals(party.getPlayers, Seq(newPlayer)) shouldEqual true
|
||||
}
|
||||
|
||||
"update bis link" in {
|
||||
val updateProbe = testKit.createTestProbe[Unit]()
|
||||
val newPlayer = Fixtures.playerEmpty.copy(priority = 2, link = Some("link"))
|
||||
|
||||
database ! UpdateBiSLink(Fixtures.playerEmpty.playerId, "link", updateProbe.ref)
|
||||
updateProbe.expectMessage(askTimeout, ())
|
||||
|
||||
val probe = testKit.createTestProbe[Option[Player]]()
|
||||
database ! GetPlayer(Fixtures.playerEmpty.playerId, probe.ref)
|
||||
probe.expectMessage(askTimeout, Some(newPlayer))
|
||||
}
|
||||
|
||||
"remove player" in {
|
||||
val updateProbe = testKit.createTestProbe[Unit]()
|
||||
database ! RemovePlayer(Fixtures.playerEmpty.playerId, updateProbe.ref)
|
||||
|
Loading…
Reference in New Issue
Block a user