package de.frosner.broccoli.controllers import javax.inject.Inject import cats.data.{EitherT, OptionT} import cats.instances.future._ import cats.syntax.either._ import com.mohiva.play.silhouette.api.util.Credentials import com.mohiva.play.silhouette.impl.providers.CredentialsProvider import de.frosner.broccoli.services.{SecurityService, WebSocketService} import jp.t2v.lab.play2.auth.{BroccoliSimpleAuthorization, LoginLogout} import play.api.{Environment, Logger} import play.api.cache.CacheApi import play.api.data.Forms._ import play.api.data._ import play.api.libs.json.Json import play.api.mvc.{Action, AnyContent, Controller, Results} import scala.concurrent.Future case class SecurityController @Inject()( override val securityService: SecurityService, override val cacheApi: CacheApi, override val playEnv: Environment, webSocketService: WebSocketService ) extends Controller with LoginLogout with BroccoliSimpleAuthorization { private val log = Logger(getClass) import scala.concurrent.ExecutionContext.Implicits.global // https://www.playframework.com/documentation/2.5.x/ScalaForms val loginForm = Form { mapping( SecurityController.UsernameFormKey -> text, SecurityController.PasswordFormKey -> text )(Credentials.apply)(Credentials.unapply) } def login: Action[AnyContent] = Action.async { implicit request => getSessionId(request).map(id => (id, webSocketService.closeConnections(id))) match { case Some((id, true)) => log.info(s"Removing websocket connection of $id due to another login") case _ => } (for { credentials <- EitherT.fromEither[Future]( loginForm.bindFromRequest().fold(Function.const(Results.BadRequest.asLeft), _.asRight)) login <- OptionT(securityService.authenticate(credentials)).toRight(Results.Unauthorized) result <- EitherT.right(gotoLoginSucceeded(login.providerKey)) user <- OptionT(resolveUser(login.providerKey)).toRight(Results.Unauthorized) } yield { val userResult = Results.Ok(Json.toJson(user)) result.copy( header = result.header.copy( headers = userResult.header.headers .get("Content-Type") .map { contentType => result.header.headers.updated("Content-Type", contentType) } .getOrElse(result.header.headers) ), body = userResult.body ) }).merge } def logout = Action.async(parse.empty) { implicit request => gotoLogoutSucceeded.andThen { case tryResult => getSessionId(request).map(id => (id, webSocketService.closeConnections(id))) match { case Some((id, true)) => log.info(s"Removing websocket connection of $id due to logout") case Some((id, false)) => log.info(s"There was no websocket connection for session $id") case None => log.info(s"No session available to logout from") } } } def verify = StackAction(parse.empty) { implicit request => Ok(loggedIn.name) } } object SecurityController { val UsernameFormKey = "username" val PasswordFormKey = "password" }