Get the client host first from x-forwarded-host header

Then use the x-forwarded-for header (doing a reverse lookup, if
possible). At last use the Host header.
This commit is contained in:
Eike Kettner 2020-10-09 11:10:22 +02:00
parent d4a2596b1f
commit df646dfabe
3 changed files with 13 additions and 16 deletions

View File

@ -11,8 +11,8 @@ case class CookieData(auth: AuthToken) {
def accountId: AccountId = auth.account def accountId: AccountId = auth.account
def asString: String = auth.asString def asString: String = auth.asString
def asCookie(cfg: Config, host: Option[String]): ResponseCookie = { def asCookie(cfg: Config): ResponseCookie = {
val domain = CookieData.getDomain(cfg, host) val domain = CookieData.getDomain(cfg)
val sec = cfg.baseUrl.scheme.exists(_.endsWith("s")) val sec = cfg.baseUrl.scheme.exists(_.endsWith("s"))
val path = cfg.baseUrl.path / "api" / "v1" / "sec" val path = cfg.baseUrl.path / "api" / "v1" / "sec"
ResponseCookie( ResponseCookie(
@ -29,8 +29,8 @@ object CookieData {
val cookieName = "docspell_auth" val cookieName = "docspell_auth"
val headerName = "X-Docspell-Auth" val headerName = "X-Docspell-Auth"
private def getDomain(cfg: Config, remote: Option[String]): Option[String] = private def getDomain(cfg: Config): Option[String] =
if (cfg.baseUrl.isLocal) remote.orElse(cfg.baseUrl.host) if (cfg.baseUrl.isLocal) None
else cfg.baseUrl.host else cfg.baseUrl.host
def authenticator[F[_]](r: Request[F]): Either[String, String] = def authenticator[F[_]](r: Request[F]): Either[String, String] =
@ -51,11 +51,11 @@ object CookieData {
.map(_.value) .map(_.value)
.toRight("Couldn't find an authenticator") .toRight("Couldn't find an authenticator")
def deleteCookie(cfg: Config, remoteHost: Option[String]): ResponseCookie = def deleteCookie(cfg: Config): ResponseCookie =
ResponseCookie( ResponseCookie(
cookieName, cookieName,
"", "",
domain = getDomain(cfg, remoteHost), domain = getDomain(cfg),
path = Some(cfg.baseUrl.path / "api" / "v1" / "sec").map(_.asString), path = Some(cfg.baseUrl.path / "api" / "v1" / "sec").map(_.asString),
httpOnly = true, httpOnly = true,
secure = cfg.baseUrl.scheme.exists(_.endsWith("s")), secure = cfg.baseUrl.scheme.exists(_.endsWith("s")),

View File

@ -9,8 +9,8 @@ import org.http4s.util.CaseInsensitiveString
object ClientHost { object ClientHost {
def get[F[_]](req: Request[F]): Option[String] = def get[F[_]](req: Request[F]): Option[String] =
xForwardedFor(req) xForwardedHost(req)
.orElse(xForwardedHost(req)) .orElse(xForwardedFor(req))
.orElse(host(req)) .orElse(host(req))
private def host[F[_]](req: Request[F]): Option[String] = private def host[F[_]](req: Request[F]): Option[String] =

View File

@ -7,7 +7,6 @@ import docspell.backend.auth._
import docspell.restapi.model._ import docspell.restapi.model._
import docspell.restserver._ import docspell.restserver._
import docspell.restserver.auth._ import docspell.restserver.auth._
import docspell.restserver.http4s.ClientHost
import org.http4s._ import org.http4s._
import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityDecoder._
@ -24,8 +23,7 @@ object LoginRoutes {
for { for {
up <- req.as[UserPass] up <- req.as[UserPass]
res <- S.loginUserPass(cfg.auth)(Login.UserPass(up.account, up.password)) res <- S.loginUserPass(cfg.auth)(Login.UserPass(up.account, up.password))
remote = ClientHost.get(req) resp <- makeResponse(dsl, cfg, res, up.account)
resp <- makeResponse(dsl, cfg, remote, res, up.account)
} yield resp } yield resp
} }
} }
@ -38,17 +36,16 @@ object LoginRoutes {
case req @ POST -> Root / "session" => case req @ POST -> Root / "session" =>
Authenticate Authenticate
.authenticateRequest(S.loginSession(cfg.auth))(req) .authenticateRequest(S.loginSession(cfg.auth))(req)
.flatMap(res => makeResponse(dsl, cfg, ClientHost.get(req), res, "")) .flatMap(res => makeResponse(dsl, cfg, res, ""))
case req @ POST -> Root / "logout" => case POST -> Root / "logout" =>
Ok().map(_.addCookie(CookieData.deleteCookie(cfg, ClientHost.get(req)))) Ok().map(_.addCookie(CookieData.deleteCookie(cfg)))
} }
} }
def makeResponse[F[_]: Effect]( def makeResponse[F[_]: Effect](
dsl: Http4sDsl[F], dsl: Http4sDsl[F],
cfg: Config, cfg: Config,
remoteHost: Option[String],
res: Login.Result, res: Login.Result,
account: String account: String
): F[Response[F]] = { ): F[Response[F]] = {
@ -66,7 +63,7 @@ object LoginRoutes {
Some(cd.asString), Some(cd.asString),
cfg.auth.sessionValid.millis cfg.auth.sessionValid.millis
) )
).map(_.addCookie(cd.asCookie(cfg, remoteHost))) ).map(_.addCookie(cd.asCookie(cfg)))
} yield resp } yield resp
case _ => case _ =>
Ok(AuthResult("", account, false, "Login failed.", None, 0L)) Ok(AuthResult("", account, false, "Login failed.", None, 0L))