Last active
March 11, 2017 09:00
-
-
Save dk8996/1fd2497e82297dad5789 to your computer and use it in GitHub Desktop.
Basic Auth Filter for Play Framework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
}) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@()(implicit request: RequestHeader) | |
<html><body>ping from: @{request.remoteAddress}</body></html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
There's a bug at line 43. When you use
split
on the string it removes what you split on, so your code above does this:What you need to do is combine the string back together with a colon like so: