UserController.scala 5.6 KB

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