Add authorization
opened this issue · comments
One thing is missing that most real world applications will need: an authorization mechanism.
Maybe checked against a User table with slick?
Hi Marcel,
That's very true. I'll try to add something when I get the time. In the
meantime, this should help, it's a free standing test and authorisation
example in one, and should give a little insight on authorisation. It's not
pretty though, but a start:
import org.specs2.Specification
import spray.testkit.Specs2RouteTest
import spray.http._
import StatusCodes._
import spray.testkit.Specs2RouteTest
import spray.json.DefaultJsonProtocol
import spray.httpx.marshalling._
import MediaTypes.application/json
import org.specs2.runner.JUnitRunner
import spray.http.HttpHeaders.
Authorization
import spray.routing.AuthenticationRequiredRejection
import spray.routing.AuthenticationFailedRejection
import spray.routing.authentication.{ UserPass, ContextAuthenticator,
BasicAuth }
import spray.routing.Directives
import spray.routing.authentication.
UserPassAuthenticator
import spray.routing.authentication.BasicUserContext
import scala.concurrent.Promise
import scala.concurrent.Future
import spray.routing.RequestContext
class CustomAuthSpec2 extends Specification with Directives with
Specs2RouteTest {
def actorRefFactory = system
def is = {
var string2NR = () // shadow implicit conversion from Spray Directives
trait
sequential ^
"Access Control" ^
"Check Bob the Builder" ! bobAccessSucceed ^
"Check Mary had a little lamb" ! maryAccessSucceed ^
"Check Nancy Pancy" ! nancyAccessSucceed ^
end
}
case class Company(id: Long, name: String)
val companies = List(Company(1, "Sobersoft"), Company(2, "Beersoft"),
Company(3, "Nuttylot"), Company(4, "The Pie Factory"))
companies.find(_.id == 1).get.name
case class CompanyUser(name: String, password: String, companyIds:
List[Long])
val companyUsers = List(CompanyUser("Bob", "TheBuilder", List(1, 2)),
CompanyUser("Mary", "LittleLamb", List(2, 3)), CompanyUser("Nancy",
"Pancy", List(4)))
def createCompanyUser(userPass: UserPass): CompanyUser = {
companyUsers.filter(c => c.name == userPass.user && c.password ==
userPass.pass) match {
case companyUser :: _ => companyUser
case _ => CompanyUser(userPass.user, userPass.pass, Nil)
}
}
object SimpleUserPassAuthenticator extends
UserPassAuthenticator[BasicUserContext] {
def apply(userPass: Option[UserPass]) = Promise.successful(
userPass match {
case Some(UserPass(user, pass)) => {
companyUsers.find(c => c.name == user).flatMap {
case x => {
if (x.password == pass) Some(BasicUserContext(user)) else None
}
}
}
case _ => None
}).future
def apply(userPass: Option[(String, String)]) = userPass.flatMap {
case (user, pass) => {
companyUsers.find(c => c.name == user).map { case x => if
(x.password == pass) BasicUserContext(user) }
}
}
}
object CustomUserPassAuthenticator extends
UserPassAuthenticator[CompanyUser] {
def apply(userPass: Option[UserPass]) = Promise.successful(
userPass match {
case Some(UserPass(user, pass)) => {
companyUsers.find(c => c.name == user).flatMap {
case x => {
if (x.password == pass) Some(createCompanyUser(UserPass(user,
pass))) else None
}
}
}
case _ => None
}).future
def apply(userPass: Option[(String, String)]) = userPass.flatMap {
case (user, pass) => {
companyUsers.find(c => c.name == user).map { case x => if
(x.password == pass) BasicUserContext(user) }
}
}
}
val companyRoute = {
get {
path("company" / LongNumber) {
(companyId: Long) =>
authenticate(BasicAuth(CustomUserPassAuthenticator, "company")) {
user =>
authorize(user.companyIds.contains(companyId)) { ctx =>
println("Hi " + user.name + " welcome to " +
companies.find(.id == companyId).get.name)
ctx.complete("Welcome to Company " + companies.find(.id ==
companyId).get.name)
}
}
}
}
}
def bobAccessSucceed = {
Get("/company/2") ~>
addHeader(Authorization(BasicHttpCredentials("Bob", "TheBuilder"))) ~>
companyRoute ~> check {
entityAs[String] must contain("Company")
}
}
def maryAccessSucceed = {
Get("/company/3") ~>
addHeader(Authorization(BasicHttpCredentials("Mary", "LittleLamb"))) ~>
companyRoute ~> check {
entityAs[String] must contain("Company")
}
}
def nancyAccessSucceed = {
Get("/company/4") ~>
addHeader(Authorization(BasicHttpCredentials("Nancy", "Pancy"))) ~>
companyRoute ~> check {
entityAs[String] must contain("Company")
}
}
}
Best regards,
Jacobus
On Mon, Aug 19, 2013 at 2:50 PM, Marcel Klemenz notifications@github.comwrote:
One thing is missing that most real world applications will need: an
authorization mechanism.
Maybe checked against a User table with slick?—
Reply to this email directly or view it on GitHubhttps://github.com//issues/1
.
Basic HTTP authorisation added. Hope this helps :-p