Skip to content

Instantly share code, notes, and snippets.

@keynmol
Created July 24, 2023 15:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save keynmol/25268b030c95eff2f073d49763f0fefc to your computer and use it in GitHub Desktop.
Save keynmol/25268b030c95eff2f073d49763f0fefc to your computer and use it in GitHub Desktop.
Basic auth file proxy
//> using dep org.http4s::http4s-ember-server::0.23.23
//> using dep org.http4s::http4s-dsl::0.23.23
//> using dep com.outr::scribe-cats::3.11.8
/** This script implements a file proxy, which puts basic auth username and
* password challenge, in order to simulate password-protected artifactory
*
* To run it, use Scala CLI: https://scala-cli.virtuslab.org/
*
* Run `scala-cli run test.scala` and follow instructions
*/
import cats.*
import cats.data.*
import cats.effect.*
import cats.effect.*
import cats.implicits.*
import org.http4s.Credentials
import org.http4s.*
import org.http4s.dsl.io.*
import org.http4s.ember.server.EmberServerBuilder
import org.http4s.headers.Authorization
import org.http4s.headers.`WWW-Authenticate`
import org.http4s.server.*
import com.comcast.ip4s.Port
case class User(name: String)
def userMiddleware(
username: String,
password: String
): AuthMiddleware[IO, User] =
val authUserEither = Kleisli { (req: Request[IO]) =>
val authHeader: Option[Authorization] = req.headers.get[Authorization]
authHeader match
case Some(Authorization(BasicCredentials(creds)))
if creds == (username, password) =>
IO(Right(User(creds._1)))
case _ => IO(Left("oops"))
}
AuthMiddleware(
authUserEither,
onFailure = AuthedRoutes
.apply[String, IO] { case req =>
OptionT.liftF(
scribe.cats.io.error(s"Rejected $req") *> Unauthorized(
`WWW-Authenticate`(Challenge("basic", "myRealm"))
)
)
}
)
end userMiddleware
import org.http4s.server.staticcontent.*
object app extends IOApp:
def run(args: List[String]) =
val (dir, port, username, password) = args match
case dir :: p :: u :: pwd :: Nil =>
(dir, Port.fromString(p).get, u, pwd)
case _ =>
scribe.error(
"Usage: <coursier-cache> <port> <username <password>" +
"\n e.g. ~/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/ 8080 sourcegraph horsies"
)
sys.exit(-1)
val authedRoutes: AuthedRoutes[User, IO] =
AuthedRoutes.of { case req as user =>
scribe.cats.io.info(s"Serving ${req.uri}") *>
fileService[IO](FileService.Config(dir)).orNotFound.run(req)
}
EmberServerBuilder
.default[IO]
.withPort(port)
.withHttpApp(userMiddleware(username, password)(authedRoutes).orNotFound)
.build
.evalTap(serv =>
scribe.cats.io
.info("Your file server is ready at: " + serv.baseUri.renderString)
)
.useForever
.as(ExitCode.Success)
end run
end app
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment