Skip to content

Instantly share code, notes, and snippets.

@EdgeCaseBerg
Forked from dk8996/BasicAuthFilter.scala
Last active April 28, 2016 21:53
Show Gist options
  • Select an option

  • Save EdgeCaseBerg/e4bdec2c2907f40b4b72 to your computer and use it in GitHub Desktop.

Select an option

Save EdgeCaseBerg/e4bdec2c2907f40b4b72 to your computer and use it in GitHub Desktop.

Revisions

  1. EdgeCaseBerg revised this gist Mar 8, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion BasicAuthFilter.scala
    Original file line number Diff line number Diff line change
    @@ -40,7 +40,7 @@ object BasicAuthFilter extends Filter with Logging {
    val usernamePassword = decodedAuthSt.split(":")
    if (usernamePassword.length >= 2) {
    //account for ":" in passwords
    return Some(usernamePassword(0), usernamePassword.splitAt(1)._2.mkString(":")))
    return Some(usernamePassword(0), usernamePassword.drop(1).mkString(":")))
    }
    None
    }
  2. EdgeCaseBerg revised this gist Mar 8, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion BasicAuthFilter.scala
    Original file line number Diff line number Diff line change
    @@ -40,7 +40,7 @@ object BasicAuthFilter extends Filter with Logging {
    val usernamePassword = decodedAuthSt.split(":")
    if (usernamePassword.length >= 2) {
    //account for ":" in passwords
    return Some(usernamePassword(0), usernamePassword.splitAt(1)._2.mkString)
    return Some(usernamePassword(0), usernamePassword.splitAt(1)._2.mkString(":")))
    }
    None
    }
  3. @dk8996 dk8996 revised this gist Jun 14, 2014. 1 changed file with 4 additions and 2 deletions.
    6 changes: 4 additions & 2 deletions BasicAuthFilter.scala
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,8 @@ import play.mvc.Results._
    import play.api.libs.concurrent.Execution.Implicits.defaultContext

    object BasicAuthFilter extends Filter with Logging {
    private lazy val unauthResult = Results.Unauthorized.withHeaders(("WWW-Authenticate", "Basic realm=\"myRealm\""))
    private lazy val unauthResult = Results.Unauthorized.withHeaders(("WWW-Authenticate",
    "Basic realm=\"myRealm\""))
    private lazy val passwordRequired = true
    private lazy val username = "someUsername"
    private lazy val password = "somePassword"
    @@ -53,7 +54,8 @@ object BasicAuthFilter extends Filter with Logging {
    false
    }

    def apply(nextFilter: (RequestHeader) => Future[SimpleResult])(requestHeader: RequestHeader): Future[SimpleResult] = {
    def apply(nextFilter: (RequestHeader) => Future[SimpleResult])(requestHeader: RequestHeader):
    Future[SimpleResult] = {
    if (!passwordRequired || isOutsideSecurityRealm(requestHeader)) {
    return nextFilter(requestHeader)
    }
  4. @dk8996 dk8996 revised this gist Jun 6, 2014. 2 changed files with 3 additions and 1 deletion.
    1 change: 0 additions & 1 deletion BasicAuthFilter.scala
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,3 @@

    import com.typesafe.scalalogging.slf4j.Logging
    import sun.misc.BASE64Decoder
    import play.api.mvc._
    3 changes: 3 additions & 0 deletions ping.scala.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    @()(implicit request: RequestHeader)

    <html><body>ping from: @{request.remoteAddress}</body></html>
  5. @dk8996 dk8996 created this gist Jun 6, 2014.
    79 changes: 79 additions & 0 deletions BasicAuthFilter.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,79 @@

    import com.typesafe.scalalogging.slf4j.Logging
    import sun.misc.BASE64Decoder
    import play.api.mvc._
    import scala.concurrent.Future
    import play.mvc.Results._
    import play.api.libs.concurrent.Execution.Implicits.defaultContext

    object BasicAuthFilter extends Filter with Logging {
    private lazy val unauthResult = Results.Unauthorized.withHeaders(("WWW-Authenticate", "Basic realm=\"myRealm\""))
    private lazy val passwordRequired = true
    private lazy val username = "someUsername"
    private lazy val password = "somePassword"
    private lazy val outsidePages = Seq("ping.html")
    //need the space at the end
    private lazy val basicSt = "basic "

    //This is needed if you are behind a load balancer or a proxy
    private def getUserIPAddress(request: RequestHeader): String = {
    return request.headers.get("x-forwarded-for").getOrElse(request.remoteAddress.toString)
    }

    private def logFailedAttempt(requestHeader: RequestHeader) = {
    logger.warn(s"IP address ${getUserIPAddress(requestHeader)} failed to log in, " +
    s"requested uri: ${requestHeader.uri}")
    }

    private def decodeBasicAuth(auth: String): Option[(String, String)] = {
    if (auth.length() < basicSt.length()) {
    return None
    }
    val basicReqSt = auth.substring(0, basicSt.length())
    if (basicReqSt.toLowerCase() != basicSt) {
    return None
    }
    val basicAuthSt = auth.replaceFirst(basicReqSt, "")
    //BESE64Decoder is not thread safe, don't make it a field of this object
    val decoder = new BASE64Decoder()
    val decodedAuthSt = new String(decoder.decodeBuffer(basicAuthSt), "UTF-8")
    val usernamePassword = decodedAuthSt.split(":")
    if (usernamePassword.length >= 2) {
    //account for ":" in passwords
    return Some(usernamePassword(0), usernamePassword.splitAt(1)._2.mkString)
    }
    None
    }

    private def isOutsideSecurityRealm(requestHeader: RequestHeader): Boolean = {
    val reqURI = requestHeader.uri
    if (reqURI.length() > 0) {
    //remove the first "/" in the uri
    return outsidePages.contains(reqURI.substring(1))
    }
    false
    }

    def apply(nextFilter: (RequestHeader) => Future[SimpleResult])(requestHeader: RequestHeader): Future[SimpleResult] = {
    if (!passwordRequired || isOutsideSecurityRealm(requestHeader)) {
    return nextFilter(requestHeader)
    }

    requestHeader.headers.get("authorization").map { basicAuth =>
    decodeBasicAuth(basicAuth) match {
    case Some((user, pass)) => {
    if (username == user && password == pass) {
    return nextFilter(requestHeader)
    }
    }
    case _ => ;
    }
    logFailedAttempt(requestHeader)
    return Future.successful(unauthResult)
    }.getOrElse({
    logFailedAttempt(requestHeader)
    Future.successful(unauthResult)
    })

    }
    }