As anyone managed to integrate Deadbolt2 with Silhouette/SecureSocial ?
I find Silhouette Authorization a bit basic and Deadbolt2 meets all the requirements.
Thanks
I've sketched out a rough approach here, but I'll evolve it to a full working example based on the play-silhouette-seed-master activator template when I get time.
Two main steps are required.
Firstly, your com.mohiva.play.silhouette.api.Identity implementation also needs to implement be.objectify.deadbolt.scala.models.Subject.
Secondly, borrow some code from com.mohiva.play.silhouette.api.RequestHandlerBuilder and integrate it into your DeadboltHandler implementation.
import javax.inject.Inject
import be.objectify.deadbolt.scala.models.Subject
import be.objectify.deadbolt.scala.{ AuthenticatedRequest, DeadboltHandler, DynamicResourceHandler }
import com.mohiva.play.silhouette.api.{ LoginInfo, RequestProvider, Silhouette }
import com.mohiva.play.silhouette.impl.authenticators.CookieAuthenticator
import models.User
import play.api.mvc.Results._
import play.api.mvc.{ Request, Result }
import utils.auth.DefaultEnv
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
class MyDeadboltHandler #Inject() (silhouette: Silhouette[DefaultEnv]) extends DeadboltHandler {
override def beforeAuthCheck[A](request: Request[A]): Future[Option[Result]] = Future.successful(None)
override def getSubject[A](request: AuthenticatedRequest[A]): Future[Option[Subject]] =
if (request.subject.isDefined) {
Future.successful(request.subject)
} else {
// this else branch is taken from com.mohiva.play.silhouette.api.RequestHandlerBuilder
silhouette.env.authenticatorService.retrieve(request).flatMap {
// A valid authenticator was found so we retrieve also the identity
case Some(a) if a.isValid =>
silhouette.env.identityService.retrieve(a.loginInfo).map(i => i)
// An invalid authenticator was found so we needn't retrieve the identity
case Some(a) if !a.isValid => Future.successful(None)
// No authenticator was found so we try to authenticate with a request provider
case None => handleRequestProviderAuthentication(request).flatMap {
// Authentication was successful, so we retrieve the identity and create a new authenticator for it
case Some(loginInfo) => silhouette.env.identityService.retrieve(loginInfo).flatMap { (i: Option[User]) =>
silhouette.env.authenticatorService.create(loginInfo)(request).map((a: CookieAuthenticator) => i)
}
// No identity and no authenticator was found
case None => Future.successful(None)
}
}
}
// this whole function is taken from com.mohiva.play.silhouette.api.RequestHandlerBuilder
private def handleRequestProviderAuthentication[B](implicit request: Request[B]): Future[Option[LoginInfo]] = {
def auth(providers: Seq[RequestProvider]): Future[Option[LoginInfo]] = {
providers match {
case Nil => Future.successful(None)
case h :: t => h.authenticate(request).flatMap {
case Some(i) => Future.successful(Some(i))
case None => if (t.isEmpty) Future.successful(None) else auth(t)
}
}
}
auth(silhouette.env.requestProviders)
}
override def onAuthFailure[A](request: AuthenticatedRequest[A]): Future[Result] =
Future.successful(request.subject.map(subject => Redirect(controllers.routes.ApplicationController.index()))
.getOrElse(Redirect(controllers.routes.SignInController.view())))
override def getDynamicResourceHandler[A](request: Request[A]): Future[Option[DynamicResourceHandler]] = Future.successful(None)
}
In your controllers, you can now use Deadbolt constraints instead of the Silhouette authorizations. For example...
class ApplicationController #Inject() (
val messagesApi: MessagesApi,
silhouette: Silhouette[DefaultEnv])
extends Controller with I18nSupport {
def index = silhouette.SecuredAction.async { implicit request =>
Future.successful(Ok(views.html.home(request.identity)))
}
}
can be replaced with
class ApplicationController #Inject() (
val messagesApi: MessagesApi,
deadbolt: ActionBuilders)
extends Controller with I18nSupport {
def index = deadbolt.SubjectPresentAction().defaultHandler() { implicit request =>
Future.successful(Ok(views.html.home(request.subject)))
}
}
Related
I am starting to develop in Scala, so I started witha really simple RESTful API using AKKA HTTP actors and then wanted to add a PostgreSQL database to "close up" the project. The thing is that somewhere in the project, a Future that is returned by a db.run method is converted into a Promise and returning me errors. When I run the Main object and start the API and hit somewhere, I get this error:
Cannot cast scala.concurrent.impl.Promise$DefaultPromise to scala.collection.immutable.Seq or Cannot cast scala.concurrent.impl.Promise$DefaultPromise to api.Item depending on which route I an hitting.
Here is the main api.scala file:
package api
import akka.actor.{Actor, ActorSystem, Props}
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.pattern.ask
import akka.util.Timeout
import api.Main.ItemActor._
import slick.jdbc.JdbcBackend.Database
import spray.json.DefaultJsonProtocol._
import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
import scala.util.{Failure, Success}
object Main extends App {
val db = Database.forConfig("scaladb");
val itemDao = new handler(db)
val system = ActorSystem("mySystem")
val itemActor = system.actorOf(Props(new ItemActor(db)))
implicit val actorSystem = system
implicit val itemFormat = jsonFormat3(Item)
implicit val timeout: Timeout = Timeout(5.seconds)
class ItemActor(db: Database) extends Actor {
import api.Main.ItemActor._
def receive = {
case CreateItem(item) =>
sender() ! itemDao.create(item)
case ReadItem(id) =>
sender() ! itemDao.read(id)
case ReadAllItems =>
sender() ! itemDao.readAll
case UpdateItem(item) =>
sender() ! itemDao.update(item)
case DeleteItem(id) =>
sender() ! itemDao.delete(id)
}
}
object ItemActor {
case class CreateItem(item: Item)
case class ReadItem(id: Int)
case object ReadAllItems
case class UpdateItem(item: Item)
case class DeleteItem(id: Int)
}
def handleResponse(futureResponse: Future[Item]): Route = {
onComplete(futureResponse) {
case Success(response) => complete(response)
case Failure(ex) => complete(StatusCodes.InternalServerError, s"An error occurred: ${ex.getMessage}")
}
}
def handleResponseSeq(futureResponse: Future[Seq[Item]]): Route = {
onComplete(futureResponse) {
case Success(response) => complete(response)
case Failure(ex) => complete(StatusCodes.InternalServerError, s"An error occurred: ${ex.getMessage}")
}
}
val routes = pathPrefix("items") {
pathEnd {
post {
entity(as[Item]) { item =>
handleResponse((itemActor ? CreateItem(item)).mapTo[Item])
}
} ~
get {
handleResponseSeq((itemActor ? ReadAllItems).mapTo[Seq[Item]])
}
} ~
path(IntNumber) { id =>
get {
handleResponse((itemActor ? ReadItem(id)).mapTo[Item])
} ~
put {
entity(as[Item]) { item =>
handleResponse((itemActor ? UpdateItem(item)).mapTo[Item])
}
} ~
delete {
handleResponse((itemActor ? DeleteItem(id)).mapTo[Item])
}
}
}
val bindRoutes = Http().bindAndHandle(routes, "localhost", 8888)
println("Server online at http://localhost:8888/")
}
Then the handler (Where I definde the methods that access the PostgreSQL database):
package api
import slick.jdbc.PostgresProfile.api._
import scala.concurrent.Future
class handler (db:Database){
val items = TableQuery[Items]
def create(item:Item): Future[Item] = {
db.run((items returning items.map(_.id.?) into ((item, id) => item.copy(id = id))) += item)
}
def read(id: Int): Future[Option[Item]] = {
db.run(items.filter(_.id === id).result.headOption)
}
def readAll: Future[Seq[Item]] = {
println((db.run(items.result)).getClass)
db.run(items.result)
}
def update(item: Item): Future[Int] = {
db.run(items.filter(_.id === item.id).update(item))
}
def delete(id: Int): Future[Int] = {
db.run(items.filter(_.id === id).delete)
}
}
And the items file:
package api
import slick.jdbc.PostgresProfile.api._
case class Item(id: Option[Int] = None, name: String, description: String)
class Items(tag: Tag) extends Table[Item](tag, "items") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def description = column[String]("description")
def * = (id.?, name, description) <> (Item.tupled, Item.unapply)
}
I've tried to use a getClass next to the db.run(items.result) in the handler file, and it prits class scala.concurrent.impl.Promise$DefaultPromise so it must be something of an implicit converter. Thanks.
You're mixing Futures and actors, which is generally not a great idea.
In your ItemActor, instead of sending the future as a reply, it's a better idea to pipe the future as a reply (the reply won't actually happen until the future is complete, that is to say, the DAO has a result).
import akka.pattern.pipe
class ItemActor(db: Database) extends Actor {
import ItemActor._
import context.dispatcher
def receive = {
case CreateItem(item) =>
itemDao.create(item).pipeTo(sender())
case ReadItem(id) =>
itemDao.read(id).pipeTo(sender())
}
}
That said, at least in this code, there doesn't really seem to be a good reason for ItemActor to exist, given that it's just forwarding operations to the DAO. Making the itemDao visible in the routes, you could just as well do:
handleResponse(itemDao.create(item))
Here: handleResponse((itemActor ? CreateItem(item)).mapTo[Item])
Actor returns Future[Item], mapTo[Item] tries to cast it to item and fails.
You want your actor to return the actual item, not Future result from db.run.
I haven't used akka in a while, but I think, something like this should work:
val replyTo = sender
...
case CreateItem(item) => itemDao.create(item).onComplete {
case Success(i) => replyTo ! i
case Failure(e) => throw e
}
...
I'm scala beginner and for now I'm trying to build a basic play/slick app (sort of user database).
It seems I've been able to build up all the stuff but data transferring to the front-end.
Here is what I have:
UserDAO.scala
class UserDao #Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends BaseDao {
import driver.api._
def entities = TableQuery[UsersTable]
def all(): Future[Seq[User]] = {
db.run(entities.result)
}
class UsersTable(tag: Tag) extends BaseTable(tag, "USER") {
def email = column[String]("email")
def password = column[String]("password")
def * = (id, email, password) <> (User.tupled, User.unapply)
}
}
Application.scala
Application #Inject()(userDAO: UserDao) extends Controller {
def users = Action.async {
val userList = userDAO.all()
userList
.map { list => Ok(list.map(elem => Json.toJson(elem : UserDto))) }
.recover { case _ => InternalServerError }
}
}
UserDTO.scala
case class UserDto(id: Long, login: String)
object UserDto {
implicit val userWriter = Json.writes[UserDto]
implicit def from(user: User): UserDto = UserDto(user.id, user.login)
}
What I don't understand is why compiler complains about .map { list => Ok(list.map(elem => Json.toJson(elem : UserDto))) } in Application.scala. It seems that I provided everything required for conversion to json. Could please, anybody, show what I'm doing wrong?
Replace Ok(list.map(elem => Json.toJson(elem : UserDto))) with Json.toJson(list: Seq[UserDto])
Application #Inject()(userDAO: UserDao) extends Controller {
def users = Action.async {
val userList = userDAO.all()
userList
.map { list => Ok(Json.toJson(list: Seq[UserDto])) }
.recover { case _ => InternalServerError }
}
}
Instead of using overloaded methods in object JSONSourceLoaderUtil, I want to switch to pattern matching style. How do I handle the resultant Try[JValue] and Future[JValue] as F[JValue]?
Imports and case classes,
import scalaz._
import Scalaz._
import org.json4s.JsonAST.{JObject, JValue}
trait DataSource
case class LocalFile(input: File) extends DataSource
case class RemoteResource(url: String, req: JValue) extends DataSource
What I have now,
object JSONSourceLoaderUtil {
def jsonFrom[F[_], S <: DataSource](source: S)(f: S => F[JValue])(implicit ev: Monad[F]): F[JValue] = ev.bind(ev.point(source))(f)
def extractFrom(source: RemoteResource): Future[JValue] = {
Future( ... ).flatMap(input => Future.fromTry(Parser.parseFromChannel(Channels.newChannel(input))))
}
def extractFrom(source: LocalFile): Try[JValue] = Parser.parseFromFile(source.input)
}
How do I convert to pattern matching style? Is there a another way to do this if I have painted myself into a corner? Thanks.
object JSONSourceLoaderUtil {
def jsonFrom[F[_], S <: DataSource](source: S)(f: S => F[JValue])(implicit ev: Monad[F]): F[JValue] = ev.bind(ev.point(source))(f)
def extractFrom(source: DataSource): F[JValue] = source match {
case RemoteResource(url, request) => Future( ... )
.flatMap(input => Future.fromTry(Parser.parseFromChannel(Channels.newChannel(input))))) // cannot convert Future to F
case LocalFile(input) => Parser.parseFromFile(input) // cannot convert Try to F
}
}
Your desired F depends on the type of the data source. So why not make this explicit?
trait DataSource[F[_]] {
def extract: F[JValue]
}
case class LocalFile(input: File) extends DataSource[Try] {
def extract = Parser.parseFromFile(input)
}
case class RemoteResource(url: String, req: JValue) extends DataSource[Future] {
def extract = Future( ... )
.flatMap(input => Future.fromTry(Parser.parseFromChannel(Channels.newChannel(input)))))
}
Removing the extract method and writing
def extractFrom[F[_]](source: DataSource[F]): F[JValue] = source match {
case RemoteResource(url, request) => Future( ... )
.flatMap(input => Future.fromTry(Parser.parseFromChannel(Channels.newChannel(input)))))
case LocalFile(input) => Parser.parseFromFile(input)
}
}
should also probably work, at least in Scala 2.12. But I find the first solution to be cleaner.
I'm trying to craft a ScalaInterceptor that looks for an X-Forwarded-Proto header, so basically if its in production or behind a proxy then Play! auto redirects to SSL.
I've run into issues with getting this code to compile, and I'm also not sure whether this will work with the SecureSocial plugin. There are specific reasons why we aren't setting SSL=true in SecureSocial.conf that I won't go into here.
Here's what I have in my Global.scala
def WithHttpsRedirect[A](action: Action[A]): Action[A] = {
Action(action.parser) { request =>
val result = action(request)
request.headers.get("X-Forwarded-Proto").collect {
case "https" =>
result
case "http" =>
val url = "https://"+request.host+request.uri
Redirect(url)
} getOrElse {
result
}
}
}
override def onRouteRequest(request: RequestHeader): Option[Handler] = {
super.onRouteRequest(request).map { handler =>
handler match {
case a: Action[_] => WithHttpsRedirect(a)
case _ => handler
}
}
}
I'm getting a compiler error after the getOrElse:
[error] found : scala.concurrent.Future[play.api.mvc.SimpleResult]
[error] required: play.api.mvc.Result
[error] result
[error] ^
Your help is greatly appreciated!
Replace:
Action(action.parser) { request =>
with:
Action.async(action.parser) { request =>
You made need to also replace:
Redirect(url)
with:
Future.successful(Redirect(url))
Changed my method of attack, and instead implemented a filter instead of overriding onRouteRequest:
In Global.scala:
object Global extends WithFilters(HttpsFilter) with GlobalSettings
then HttpsFilter.scala:
import play.api.mvc.Results._
import play.api.mvc.{SimpleResult, RequestHeader, Filter}
import scala.concurrent._
import ExecutionContext.Implicits.global
object HttpsFilter extends Filter {
def apply(next: (RequestHeader) => Future[SimpleResult])(request: RequestHeader): Future[SimpleResult] = {
request.headers.get("X-Forwarded-Proto").collect {
case "https" =>
next(request)
case "http" =>
val url = "https://"+request.host+request.uri
Future{ Redirect(url) }
} getOrElse {
next(request)
}
}
}
I'm trying to implement authentication in my Play 2.2.1 app, and I can't quite figure out how to make it work with an action that returns a Future[Result].
This post describes pretty close to what I'm trying to do, except without returning Future[Result]:
Play 2.0 Framework, using a BodyParser with an authenticated request
How can I get it to work with Futures? I.e. how would I implement this function:
def IsAuthenticated(f: => String => Request[Any] => Future[Result])
or, better yet, this function:
def IsAuthenticated[A}(b:BodyParser[A])(f: => String => Request[Any] => Future[Result])
which would feed into this function:
def AuthenticatedUser(g: Account => Request[AnyContent] => SimpleResult) = IsAuthenticated {...}
to wrap asynchronous actions in my controllers?
This part I can do:
def IsAuthenticated(f: => String => Request[AnyContent] => Future[SimpleResult]) = {
Security.Authenticated(email, onUnauthorized) {
user => Action.async(request => f(user)(request))
}
}
But if I try to use IsAuthenticated in my wrapper function:
def AuthenticatedUser(g: Account => Request[AnyContent] => Future[SimpleResult]) = IsAuthenticated {
email => implicit request => Account.find(email).map {
opt => opt match {
case Some(account) => g(account)(request)
case None => Future(onUnauthorized(request))
}
}
}
(Account.find returns a Future[Option[Account]] 'cause it's a mongodb call that may take some time. The desire to do the future thing right is what's causing me so much grief now)
I can't get AuthenticatedUser to satisfy the compiler. It says it's getting a Future[Future[SimpleResult]] instead of a Future[SimpleResult].
So, how best to build this whole thing? I need to be able to make authentication wrappers that rely on db calls that are asynchronous.
I'm sure I'm just dense and missing something obvious...
EDIT: Here's what I ended up with. Thank you Jean for pointing me in the right direction.
I found AuthenticatedController while rooting around and it's pretty close to what I'm looking for. I wanted two types of authentication: User (authenticated user) and Administrator (to wrap code for admin tasks).
package controllers
import models.Account
import play.api.mvc._
import scala.concurrent.Future
trait Secured {
class AuthenticatedRequest[A](val account: Account, request: Request[A]) extends WrappedRequest[A](request)
object User extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
request.session.get("email") match {
case Some(email) => {
Account.find(email).flatMap {
case Some(account) => {
block(new AuthenticatedRequest(account, request))
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
}
object Administrator extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
request.session.get("email") match {
case Some(email) => {
Account.find(email).flatMap {
case Some(account) => if (account.admin) {
block(new AuthenticatedRequest(account, request))
} else {
Future(Results.Redirect(routes.Index.index()))
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
}
}
There have been changes in play 2.2 to make it easier to compose actions. The resource you are referring to is outdated.
Instead you should create a custom action builder by extending ActionBuilder to create your action, this will get you all the fancy apply methods you may need (including async support and all)
For example you may do :
trait MyAction extends Results{
class MyActionBuilder[A] extends ActionBuilder[({ type R[A] = Request[A] })#R] {
def invokeBlock[A](request: Request[A],
block: Request[A] => Future[SimpleResult]) ={
// your authentication code goes here :
request.cookies.get("loggedIn").map { _=>
block(request)
} getOrElse Future.successful(Unauthorized)
}
}
object MyAction extends MyActionBuilder
}
which you can then use as such :
object MyController extends Controller with MyAction{
def authenticatedAction=MyAction {
Ok
}
def asyncAuthenticatedAction=MyAction.async {
Future.successful(Ok)
}
def authenticatedActionWithBodyParser = MyAction(parse.json){ request =>
Ok(request.body)
}
}
For brevity's sake I used a very trivial authentication mechanism you will want to change that :)
Additionally, you can create a custom "request" type to provide additional information. For instance you could define a AuthenticatedRequest as such :
case class AuthenticatedRequest[A](user: User, request: Request[A]) extends WrappedRequest(request)
Provided you have a way to get your user such as
object User{
def find(s:String): Option[User] = ???
}
Then change your builder definition a bit as such
class MyActionBuilder[A] extends
ActionBuilder[({ type R[A] = AuthenticatedRequest[A] })#R] {
def invokeBlock[A](request: Request[A],
block: AuthenticatedRequest[A] => Future[SimpleResult]) ={
// your authentication code goes here :
(for{
userId <- request.cookies.get("userId")
user <- User.find(userId.value)
}yield {
block(AuthenticatedRequest(user,request))
}) getOrElse Future.successful(Unauthorized)
}
}
Your controller now has access to your user in authenticatedActions:
object MyController extends Controller with MyAction{
val logger = Logger("application.controllers.MyController")
def authenticatedAction=MyAction { authenticatedRequest =>
val user = authenticatedRequest.user
logger.info(s"User(${user.id} is accessing the authenticatedAction")
Ok(user.id)
}
def asyncAuthenticatedAction = MyAction.async { authenticatedRequest=>
Future.successful(Ok(authenticatedRequest.user.id))
}
def authenticatedActionWithBodyParser = MyAction(parse.json){ authenticatedRequest =>
Ok(authenticatedRequest.body)
}
}