UserController.scala 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. package com.weEat.controllers
  2. import java.util.Base64
  3. import javax.inject.{Inject,Singleton}
  4. import play.api._
  5. import play.api.mvc._
  6. import play.api.libs.json._
  7. import com.weEat.services.OAuth2Service
  8. import com.weEat.models.{Authorization,User}
  9. import com.weEat.shared.models.UserRegistration
  10. import com.github.t3hnar.bcrypt._
  11. import scalaoauth2.provider._
  12. import scala.concurrent.duration._
  13. import scala.concurrent.{Await,Future,blocking}
  14. import scala.util.{Try,Success,Failure}
  15. import org.mongodb.scala.ObservableImplicits
  16. import com.weEat.services.MongoDBService
  17. import org.mongodb.scala.model.Filters._
  18. import org.bson.types.ObjectId
  19. import org.mongodb.scala.MongoCollection
  20. @Singleton
  21. class UserController @Inject()(
  22. val controllerComponents: ControllerComponents,
  23. oauth: OAuth2Service,
  24. db: MongoDBService
  25. ) extends BaseController
  26. with OAuth2Provider
  27. with ObservableImplicits
  28. with OAuth2ProviderActionBuilders {
  29. implicit val ec = scala.concurrent.ExecutionContext.global
  30. import db.withCollection
  31. override val tokenEndpoint = new TokenEndpoint {
  32. override val handlers = Map(
  33. //OAuthGrantType.AUTHORIZATION_CODE -> new AuthorizationCode(),
  34. OAuthGrantType.REFRESH_TOKEN -> new RefreshToken(),
  35. //OAuthGrantType.CLIENT_CREDENTIALS -> new ClientCredentials(),
  36. //OAuthGrantType.IMPLICIT -> new Implicit(),
  37. OAuthGrantType.PASSWORD -> new Password()
  38. )
  39. }
  40. def encodeBasicAuth(email: String, pass: String) =
  41. s"Basic " + Base64.getEncoder().encodeToString(s"$email:$pass".getBytes())
  42. def decodeBasicAuth(auth: String): Option[(String, String)] = {
  43. val format = raw"Basic ([\d\w+/=]*)".r
  44. auth match {
  45. case format(cred) => {
  46. val split = new String(Base64.getDecoder().decode(cred)).split(":", 2)
  47. Some((split(0), split(1)))
  48. }
  49. case _ => None
  50. }
  51. }
  52. def accessToken = Action.async { implicit request: Request[Any] =>
  53. issueAccessToken(oauth)
  54. }
  55. def revokeAccessToken() =
  56. AuthorizedAction[Authorization](oauth).async(parse.json)
  57. { implicit request: AuthInfoRequest[JsValue, Authorization] =>
  58. val email = (request.body \ "client_id").as[String]
  59. val refresh = (request.body \ "refresh_token").as[String]
  60. oauth.revokeAccessToken(request.authInfo, email, refresh).transform {
  61. case Success(_) => Success(
  62. Ok("").withHeaders("Cache-Control" -> "no-store", "Pragma" -> "no-cache")
  63. )
  64. case Failure(e: OAuthError) => Success(
  65. new Status(e.statusCode)(responseOAuthErrorJson(e))
  66. .withHeaders(responseOAuthErrorHeader(e))
  67. )
  68. case Failure(e) => Failure(e)
  69. }
  70. }
  71. private val emailRegex = """(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])"""
  72. private def isEmail(email: String): Boolean = email.matches(emailRegex)
  73. // private def checkEmail(conn: Connection, email: String): Future[Boolean] = {
  74. // val emailquery = "SELECT id FROM users WHERE email=? LIMIT 1"
  75. // Future {
  76. // TryWith(conn.prepareStatement(emailquery)) { st =>
  77. // st.setString(1, email)
  78. // st.executeQuery.next
  79. // }
  80. // }.transform(_.flatten)
  81. // }
  82. def validateRegisterRequest(body: UserRegistration) =
  83. if (!isEmail(body.email))
  84. Some("Invalid email.")
  85. else if (body.password.length < 8)
  86. Some("Password too short (Minimum 8 characters).")
  87. else
  88. None
  89. // TODO: Unit test API
  90. def registerUser() = Action.async(parse.json)
  91. { implicit request: Request[JsValue] =>
  92. val body = request.body.as[UserRegistration]
  93. validateRegisterRequest(body) match {
  94. case Some(reason) => Future.successful(BadRequest(reason))
  95. case None => {
  96. val user = User(body)
  97. withCollection(User) { collection =>
  98. {
  99. collection.insertOne(user).head().map(res =>
  100. {
  101. issueAccessToken(oauth)(Request[Map[String, Seq[String]]](
  102. request.withHeaders(Headers(
  103. "Authorization" -> encodeBasicAuth(user.email, body.password)
  104. )),
  105. Map("grant_type" -> Seq(OAuthGrantType.PASSWORD))
  106. ), ec)}
  107. ).flatten}
  108. }.flatten
  109. }
  110. }
  111. }
  112. def getName() = AuthorizedAction[Authorization](oauth).async
  113. { implicit request: AuthInfoRequest[AnyContent, Authorization] =>
  114. getUser(request.authInfo.user.userId).map({
  115. case Some(user) => Ok("%s %s".format(user.fname, user.lname))
  116. case None =>
  117. throw new IllegalStateException("Authorized user does not exist!")
  118. })
  119. }
  120. // TODO: Unit test API
  121. def getUsers() = Action.async
  122. { implicit request: Request[AnyContent] =>
  123. withCollection(User) { collection =>
  124. collection.find().map(res => res.email)
  125. .toFuture()
  126. .transform({
  127. case Success(x) => Success(Ok(Json.toJson(x)))
  128. case Failure(x) => throw x
  129. })
  130. }.flatten
  131. }
  132. def getUser(id: ObjectId): Future[Option[User]] = withCollection(User) { users =>
  133. users.find(equal("_id", id))
  134. .first()
  135. .toFutureOption()
  136. }.flatten
  137. def get(id: String) = Action.async { implicit request: Request[AnyContent] =>
  138. getUser(new ObjectId(id)).map({
  139. case Some(user) => Ok(Json.toJson(user.toShared()))
  140. case None => NotFound("No such user with this id")
  141. })
  142. }
  143. }