Skip to content

Commit

Permalink
Purge last usages of SqlToken.raw with user input (#6745)
Browse files Browse the repository at this point in the history
* Purge last usages of SqlToken.raw with user input

* Update SqlInterpolation.scala

* fix tests, add test for enum array

* Update app/utils/sql/SqlInterpolation.scala

Co-authored-by: Norman Rzepka <[email protected]>
  • Loading branch information
fm3 and normanrz authored Jan 17, 2023
1 parent a492ffe commit 8cae093
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 34 deletions.
3 changes: 1 addition & 2 deletions app/models/binary/DataSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,7 @@ class DataSetDAO @Inject()(sqlClient: SqlClient,
case None => q"${true}"
case Some(searchQuery) =>
val queryTokens = searchQuery.toLowerCase.trim.split(" +")
SqlToken.raw(
queryTokens.map(queryToken => s"POSITION(${escapeLiteral(queryToken)} IN LOWER(name)) > 0").mkString(" AND "))
SqlToken.joinBySeparator(queryTokens.map(queryToken => q"POSITION($queryToken IN LOWER(name)) > 0"), " AND ")
}

def countByFolder(folderId: ObjectId): Fox[Int] =
Expand Down
18 changes: 13 additions & 5 deletions app/models/user/MultiUser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ package models.user

import com.mohiva.play.silhouette.api.util.PasswordInfo
import com.scalableminds.util.accesscontext.DBAccessContext
import com.scalableminds.util.enumeration.ExtendedEnumeration
import com.scalableminds.util.time.Instant
import com.scalableminds.util.tools.{Fox, JsonHelper}

import javax.inject.Inject
import com.scalableminds.webknossos.schema.Tables._
import models.user.Theme.Theme
import play.api.libs.json.Format.GenericFormat
import play.api.libs.json.{JsObject, Json}
import slick.lifted.Rep
import utils.sql.{SQLDAO, SqlClient, SqlToken}
import utils.ObjectId
import utils.sql.{SQLDAO, SqlClient}

import javax.inject.Inject
import scala.concurrent.ExecutionContext

case class MultiUser(
Expand All @@ -28,6 +28,12 @@ case class MultiUser(
isDeleted: Boolean = false
)

object PasswordHasherType extends ExtendedEnumeration {
type PasswordHasher = Value

val SCrypt, Empty = Value
}

class MultiUserDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext)
extends SQLDAO[MultiUser, MultiusersRow, Multiusers](sqlClient) {
protected val collection = Multiusers
Expand Down Expand Up @@ -55,11 +61,12 @@ class MultiUserDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext

def insertOne(u: MultiUser): Fox[Unit] =
for {
passwordInfoHasher <- PasswordHasherType.fromString(u.passwordInfo.hasher).toFox
_ <- run(q"""insert into webknossos.multiusers(_id, email, passwordInfo_hasher,
passwordInfo_password,
isSuperUser, novelUserExperienceInfos, selectedTheme,
created, isDeleted)
values(${u._id}, ${u.email}, ${SqlToken.raw(escapeLiteral(u.passwordInfo.hasher))},
values(${u._id}, ${u.email}, $passwordInfoHasher,
${u.passwordInfo.password},
${u.isSuperUser}, ${u.novelUserExperienceInfos}, ${u.selectedTheme},
${u.created}, ${u.isDeleted})""".asUpdate)
Expand All @@ -68,8 +75,9 @@ class MultiUserDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext
def updatePasswordInfo(multiUserId: ObjectId, passwordInfo: PasswordInfo)(implicit ctx: DBAccessContext): Fox[Unit] =
for {
_ <- assertUpdateAccess(multiUserId)
passwordInfoHasher <- PasswordHasherType.fromString(passwordInfo.hasher).toFox
_ <- run(q"""update webknossos.multiusers set
passwordInfo_hasher = ${SqlToken.raw(escapeLiteral(passwordInfo.hasher))},
passwordInfo_hasher = $passwordInfoHasher,
passwordInfo_password = ${passwordInfo.password}
where _id = $multiUserId""".asUpdate)
} yield ()
Expand Down
12 changes: 10 additions & 2 deletions app/oxalis/security/Token.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ package oxalis.security
import com.mohiva.play.silhouette.api.LoginInfo
import com.mohiva.play.silhouette.impl.authenticators.BearerTokenAuthenticator
import com.scalableminds.util.accesscontext.DBAccessContext
import com.scalableminds.util.enumeration.ExtendedEnumeration
import com.scalableminds.util.time.Instant
import com.scalableminds.util.tools.Fox
import com.scalableminds.webknossos.schema.Tables._
import oxalis.security.TokenType.TokenType
import slick.jdbc.PostgresProfile.api._
import slick.lifted.Rep
import utils.sql.{SQLDAO, SqlClient, SqlToken}
import utils.ObjectId
import utils.sql.{SQLDAO, SqlClient}

import javax.inject.Inject
import scala.concurrent.ExecutionContext
Expand All @@ -37,6 +38,12 @@ case class Token(_id: ObjectId,
))
}

object LoginInfoProvider extends ExtendedEnumeration {
type PasswordHasher = Value

val credentials: LoginInfoProvider.Value = Value
}

object Token {
def fromBearerTokenAuthenticator(b: BearerTokenAuthenticator, tokenType: TokenType)(
implicit ec: ExecutionContext): Fox[Token] =
Expand Down Expand Up @@ -97,9 +104,10 @@ class TokenDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext)

def insertOne(t: Token): Fox[Unit] =
for {
loginInfoProvider <- LoginInfoProvider.fromString(t.loginInfo.providerID).toFox
_ <- run(
q"""insert into webknossos.tokens(_id, value, loginInfo_providerID, loginInfo_providerKey, lastUsedDateTime, expirationDateTime, idleTimeout, tokenType, created, isDeleted)
values(${t._id}, ${t.value}, ${SqlToken.raw(escapeLiteral(t.loginInfo.providerID))},
values(${t._id}, ${t.value}, $loginInfoProvider,
${t.loginInfo.providerKey}, ${t.lastUsedDateTime},
${t.expirationDateTime}, ${t.idleTimeout.map(_.toMillis)},
${t.tokenType}, ${t.created}, ${t.isDeleted})""".asUpdate)
Expand Down
23 changes: 3 additions & 20 deletions app/utils/sql/SqlInterpolation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -74,26 +74,6 @@ case class SqlToken(sql: String, values: List[SqlValue] = List()) {
}

object SqlToken {
def join(values: List[Either[SqlValue, SqlToken]], sep: String): SqlToken = {
val outputSql = mutable.StringBuilder.newBuilder
val outputValues = ListBuffer[SqlValue]()
for (i <- values.indices) {
val value = values(i)
value match {
case Left(x) =>
outputSql ++= x.placeholder
outputValues += x
case Right(x) =>
outputSql ++= x.sql
outputValues ++= x.values
}
if (i < values.length - 1) {
outputSql ++= sep
}
}
SqlToken(sql = outputSql.toString, values = outputValues.toList)
}

def tupleFromList(values: List[SqlValue]): SqlToken =
SqlToken(sql = s"(${values.map(_.placeholder).mkString(", ")})", values = values)

Expand All @@ -106,6 +86,9 @@ object SqlToken {

def raw(s: String): SqlToken = SqlToken(s)

def joinBySeparator(tokens: Iterable[SqlToken], separator: String): SqlToken =
SqlToken(sql = tokens.map(_.sql).mkString(separator), values = tokens.flatMap(_.values).toList)

def empty: SqlToken = raw("")

def identifier(id: String): SqlToken = raw('"' + id + '"')
Expand Down
16 changes: 11 additions & 5 deletions test/backend/SqlInterpolationTestSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ class SqlInterpolationTestSuite extends PlaySpec with SqlTypeImplicits {
assert(sql == SqlToken("SELECT * FROM test WHERE state = ?", List(EnumerationValue(enumVal))))
assert(sql.debugInfo == "SELECT * FROM test WHERE state = 'PENDING'")
}
"construct an SQLToken with Enumeration Array" in {
val enumVals = List(JobState.PENDING, JobState.STARTED)
val sql = q"""SELECT * FROM test WHERE state = ${EnumerationArrayValue(enumVals, "webknossos.JOB_STATE")}"""
assert(
sql == SqlToken("SELECT * FROM test WHERE state = ?::webknossos.JOB_STATE[]",
List(EnumerationArrayValue(enumVals, "webknossos.JOB_STATE"))))
assert(sql.debugInfo == "SELECT * FROM test WHERE state = {PENDING,STARTED}::webknossos.JOB_STATE[]")
}
"construct an SQLToken with String Array" in {
val stringList = List("First String", "Second String")
val sql = q"""SELECT * FROM test WHERE tags = $stringList"""
Expand All @@ -163,14 +171,12 @@ class SqlInterpolationTestSuite extends PlaySpec with SqlTypeImplicits {
assert(sql.debugInfo == "SELECT * FROM test WHERE bounding_box = '(1.0,2.0,3.0,50.0,60.0,70.0)'")
}
"construct an SQLToken with nested-joined SQL" in {
val fields = List("name", "age")
val fields = List(q"name", q"age")
val values = List("Bob".toSqlValue, 5.toSqlValue)
val sql =
q"""INSERT INTO test(${SqlToken.join(fields.map(x => Right(SqlToken.identifier(x))), ", ")}) VALUES ${SqlToken
.tupleList(List(values))}"""
q"""INSERT INTO test(${SqlToken.joinBySeparator(fields, ", ")}) VALUES ${SqlToken.tupleList(List(values))}"""

assert(
sql == SqlToken("""INSERT INTO test("name", "age") VALUES (?, ?)""", List(StringValue("Bob"), IntValue(5))))
assert(sql == SqlToken("""INSERT INTO test(name, age) VALUES (?, ?)""", List(StringValue("Bob"), IntValue(5))))
}
"create debugInfo from SQLToken" in {
val sql = q"""SELECT * FROM test WHERE age = ${3} AND name = ${"Amy"}"""
Expand Down

0 comments on commit 8cae093

Please sign in to comment.