-
-
Save EdgeCaseBerg/e4bdec2c2907f40b4b72 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.drop(1).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
Quick question...couldn't you use a flatMap on 64 and replace line 70 with
case _ => None
?