mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-21 09:58:26 +00:00
Update dependencies
This commit is contained in:
@ -8,51 +8,78 @@ import cats.data.NonEmptyList
|
||||
import cats.effect.{Blocker, ContextShift, Sync}
|
||||
import docspell.common.LenientUri.Path
|
||||
import io.circe.{Decoder, Encoder}
|
||||
import java.net.URLEncoder
|
||||
import scodec.bits.ByteVector
|
||||
import cats.effect.Resource
|
||||
import java.net.HttpURLConnection
|
||||
|
||||
/** A URI.
|
||||
*
|
||||
* It is not compliant to rfc3986, but covers most use cases in a convenient way.
|
||||
*/
|
||||
case class LenientUri(scheme: NonEmptyList[String]
|
||||
, authority: Option[String]
|
||||
, path: LenientUri.Path
|
||||
, query: Option[String]
|
||||
, fragment: Option[String]) {
|
||||
case class LenientUri(
|
||||
scheme: NonEmptyList[String],
|
||||
authority: Option[String],
|
||||
path: LenientUri.Path,
|
||||
query: Option[String],
|
||||
fragment: Option[String]
|
||||
) {
|
||||
|
||||
def /(segment: String): LenientUri =
|
||||
copy(path = path / segment)
|
||||
|
||||
def ++ (np: Path): LenientUri =
|
||||
def ++(np: Path): LenientUri =
|
||||
copy(path = np.segments.foldLeft(path)(_ / _))
|
||||
|
||||
def ++ (np: String): LenientUri = {
|
||||
def ++(np: String): LenientUri = {
|
||||
val rel = LenientUri.stripLeading(np, '/')
|
||||
++(LenientUri.unsafe(s"a:$rel").path)
|
||||
}
|
||||
|
||||
def withQuery(name: String, value: String): LenientUri =
|
||||
withQueryPlain(name, URLEncoder.encode(value, "UTF-8"))
|
||||
|
||||
def withQueryPlain(name: String, value: String): LenientUri =
|
||||
copy(query = query.map(q => q + "&" + name + "=" + value).orElse(Option(s"$name=$value")))
|
||||
|
||||
def withFragment(f: String): LenientUri =
|
||||
copy(fragment = Some(f))
|
||||
|
||||
def toJavaUrl: Either[String, URL] =
|
||||
Either.catchNonFatal(new URL(asString)).left.map(_.getMessage)
|
||||
|
||||
def readURL[F[_]: Sync : ContextShift](chunkSize: Int, blocker: Blocker): Stream[F, Byte] =
|
||||
Stream.emit(Either.catchNonFatal(new URL(asString))).
|
||||
covary[F].
|
||||
rethrow.
|
||||
flatMap(url => fs2.io.readInputStream(Sync[F].delay(url.openStream()), chunkSize, blocker, true))
|
||||
def open[F[_]: Sync]: Either[String, Resource[F, HttpURLConnection]] =
|
||||
toJavaUrl.map { url =>
|
||||
Resource
|
||||
.make(Sync[F].delay(url.openConnection().asInstanceOf[HttpURLConnection]))(
|
||||
conn => Sync[F].delay(conn.disconnect())
|
||||
)
|
||||
}
|
||||
|
||||
def readURL[F[_]: Sync: ContextShift](chunkSize: Int, blocker: Blocker): Stream[F, Byte] =
|
||||
Stream
|
||||
.emit(Either.catchNonFatal(new URL(asString)))
|
||||
.covary[F]
|
||||
.rethrow
|
||||
.flatMap(
|
||||
url => fs2.io.readInputStream(Sync[F].delay(url.openStream()), chunkSize, blocker, true)
|
||||
)
|
||||
|
||||
def host: Option[String] =
|
||||
authority.
|
||||
map(a => a.indexOf(':') match {
|
||||
case -1 => a
|
||||
case n => a.substring(0, n)
|
||||
})
|
||||
authority.map(
|
||||
a =>
|
||||
a.indexOf(':') match {
|
||||
case -1 => a
|
||||
case n => a.substring(0, n)
|
||||
}
|
||||
)
|
||||
|
||||
def asString: String = {
|
||||
val schemePart = scheme.toList.mkString(":")
|
||||
val authPart = authority.map(a => s"//$a").getOrElse("")
|
||||
val pathPart = path.asString
|
||||
val queryPart = query.map(q => s"?$q").getOrElse("")
|
||||
val fragPart = fragment.map(f => s"#$f").getOrElse("")
|
||||
val authPart = authority.map(a => s"//$a").getOrElse("")
|
||||
val pathPart = path.asString
|
||||
val queryPart = query.map(q => s"?$q").getOrElse("")
|
||||
val fragPart = fragment.map(f => s"#$f").getOrElse("")
|
||||
s"$schemePart:$authPart$pathPart$queryPart$fragPart"
|
||||
}
|
||||
}
|
||||
@ -68,30 +95,30 @@ object LenientUri {
|
||||
}
|
||||
case object RootPath extends Path {
|
||||
val segments = Nil
|
||||
val isRoot = true
|
||||
val isEmpty = false
|
||||
val isRoot = true
|
||||
val isEmpty = false
|
||||
def /(seg: String): Path =
|
||||
NonEmptyPath(NonEmptyList.of(seg))
|
||||
def asString = "/"
|
||||
}
|
||||
case object EmptyPath extends Path {
|
||||
val segments = Nil
|
||||
val isRoot = false
|
||||
val isEmpty = true
|
||||
val isRoot = false
|
||||
val isEmpty = true
|
||||
def /(seg: String): Path =
|
||||
NonEmptyPath(NonEmptyList.of(seg))
|
||||
def asString = ""
|
||||
}
|
||||
case class NonEmptyPath(segs: NonEmptyList[String]) extends Path {
|
||||
def segments = segs.toList
|
||||
val isEmpty = false
|
||||
val isRoot = false
|
||||
val isEmpty = false
|
||||
val isRoot = false
|
||||
def /(seg: String): Path =
|
||||
copy(segs = segs.append(seg))
|
||||
def asString = segs.head match {
|
||||
case "." => segments.map(percentEncode).mkString("/")
|
||||
case "." => segments.map(percentEncode).mkString("/")
|
||||
case ".." => segments.map(percentEncode).mkString("/")
|
||||
case _ => "/" + segments.map(percentEncode).mkString("/")
|
||||
case _ => "/" + segments.map(percentEncode).mkString("/")
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,11 +131,12 @@ object LenientUri {
|
||||
def parse(str: String): Either[String, LenientUri] = {
|
||||
def makePath(str: String): Path = str.trim match {
|
||||
case "/" => RootPath
|
||||
case "" => EmptyPath
|
||||
case _ => NonEmptyList.fromList(stripLeading(str, '/').split('/').toList.map(percentDecode)) match {
|
||||
case Some(nl) => NonEmptyPath(nl)
|
||||
case None => sys.error(s"Invalid url: $str")
|
||||
}
|
||||
case "" => EmptyPath
|
||||
case _ =>
|
||||
NonEmptyList.fromList(stripLeading(str, '/').split('/').toList.map(percentDecode)) match {
|
||||
case Some(nl) => NonEmptyPath(nl)
|
||||
case None => sys.error(s"Invalid url: $str")
|
||||
}
|
||||
}
|
||||
|
||||
def makeNonEmpty(str: String): Option[String] =
|
||||
@ -128,9 +156,13 @@ object LenientUri {
|
||||
case n =>
|
||||
pqf.indexOf('#', n) match {
|
||||
case -1 =>
|
||||
(makePath(pqf.substring(0, n)), makeNonEmpty(pqf.substring(n+1)), None)
|
||||
(makePath(pqf.substring(0, n)), makeNonEmpty(pqf.substring(n + 1)), None)
|
||||
case k =>
|
||||
(makePath(pqf.substring(0, n)), makeNonEmpty(pqf.substring(n+1, k)), makeNonEmpty(pqf.substring(k+1)))
|
||||
(
|
||||
makePath(pqf.substring(0, n)),
|
||||
makeNonEmpty(pqf.substring(n + 1, k)),
|
||||
makeNonEmpty(pqf.substring(k + 1))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,7 +172,7 @@ object LenientUri {
|
||||
val scheme = makeScheme(p0)
|
||||
val (auth, pathQF) = p1.indexOf('/') match {
|
||||
case -1 => (Some(p1), "")
|
||||
case n => (Some(p1.substring(0, n)), p1.substring(n))
|
||||
case n => (Some(p1.substring(0, n)), p1.substring(n))
|
||||
}
|
||||
val (path, query, frag) = splitPathQF(pathQF)
|
||||
scheme match {
|
||||
@ -155,7 +187,7 @@ object LenientUri {
|
||||
case -1 =>
|
||||
Left(s"No scheme found: $str")
|
||||
case n =>
|
||||
val scheme = makeScheme(p0.substring(0, n))
|
||||
val scheme = makeScheme(p0.substring(0, n))
|
||||
val (path, query, frag) = splitPathQF(p0.substring(n + 1))
|
||||
scheme match {
|
||||
case None =>
|
||||
@ -167,16 +199,13 @@ object LenientUri {
|
||||
}
|
||||
}
|
||||
|
||||
private[this] val delims: Set[Char] = ",/?:@&=+$# %".toSet
|
||||
private[this] val delims: Set[Char] = ",/?:@&$# %".toSet
|
||||
|
||||
private def percent(s: String): String =
|
||||
"%" + ByteVector.encodeUtf8(s).
|
||||
fold(throw _, identity).
|
||||
toHex
|
||||
"%" + ByteVector.encodeUtf8(s).fold(throw _, identity).toHex
|
||||
|
||||
def percentEncode(s: String): String =
|
||||
s.flatMap(c =>
|
||||
if (delims.contains(c)) percent(c.toString) else c.toString)
|
||||
s.flatMap(c => if (delims.contains(c)) percent(c.toString) else c.toString)
|
||||
|
||||
def percentDecode(s: String): String =
|
||||
if (!s.contains("%")) s
|
||||
@ -188,13 +217,14 @@ object LenientUri {
|
||||
else if (c == '%') ("%", res)
|
||||
else (acc, res :+ c.toByte)
|
||||
}
|
||||
._2.decodeUtf8.fold(throw _, identity)
|
||||
._2
|
||||
.decodeUtf8
|
||||
.fold(throw _, identity)
|
||||
|
||||
private def stripLeading(s: String, c: Char): String =
|
||||
if (s.length > 0 && s.charAt(0) == c) s.substring(1)
|
||||
else s
|
||||
|
||||
|
||||
implicit val encodeLenientUri: Encoder[LenientUri] =
|
||||
Encoder.encodeString.contramap(_.asString)
|
||||
|
||||
|
@ -26,7 +26,7 @@ object Authenticate {
|
||||
val authUser = getUser[F](S.loginSession(cfg))
|
||||
|
||||
val onFailure: AuthedRoutes[String, F] =
|
||||
Kleisli(req => OptionT.liftF(Forbidden(req.authInfo)))
|
||||
Kleisli(req => OptionT.liftF(Forbidden(req.context)))
|
||||
|
||||
val middleware: AuthMiddleware[F, AuthToken] =
|
||||
AuthMiddleware(authUser, onFailure)
|
||||
@ -41,12 +41,12 @@ object Authenticate {
|
||||
val authUser = getUser[F](S.loginSession(cfg))
|
||||
|
||||
val onFailure: AuthedRoutes[String, F] =
|
||||
Kleisli(req => OptionT.liftF(Forbidden(req.authInfo)))
|
||||
Kleisli(req => OptionT.liftF(Forbidden(req.context)))
|
||||
|
||||
val middleware: AuthMiddleware[F, AuthToken] =
|
||||
AuthMiddleware(authUser, onFailure)
|
||||
|
||||
middleware(AuthedRoutes(authReq => f(authReq.authInfo).run(authReq.req)))
|
||||
middleware(AuthedRoutes(authReq => f(authReq.context).run(authReq.req)))
|
||||
}
|
||||
|
||||
private def getUser[F[_]: Effect](auth: String => F[Login.Result]): Kleisli[F, Request[F], Either[String, AuthToken]] =
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
CREATE TABLE "filemeta" (
|
||||
"id" varchar(254) not null primary key,
|
||||
"timestamp" varchar(40) not null,
|
||||
|
Reference in New Issue
Block a user