introduce lila.common.Bearer newtype
parent
a32038828c
commit
0327f65be9
|
@ -51,7 +51,7 @@ final class BulkPairing(env: Env) extends LilaController(env) {
|
|||
Json.obj(
|
||||
"tokens" -> JsObject {
|
||||
tokens.map { case BadToken(token, error) =>
|
||||
token.value -> JsString(error.message)
|
||||
token.secret -> JsString(error.message)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -9,13 +9,12 @@ import views.html
|
|||
import lila.api.Context
|
||||
import lila.app._
|
||||
import lila.challenge.{ Challenge => ChallengeModel }
|
||||
import lila.common.{ HTTPRequest, IpAddress }
|
||||
import lila.common.{ Bearer, HTTPRequest, IpAddress, Template }
|
||||
import lila.game.{ AnonCookie, Pov }
|
||||
import lila.oauth.{ AccessToken, OAuthScope }
|
||||
import lila.setup.ApiConfig
|
||||
import lila.socket.Socket.SocketVersion
|
||||
import lila.user.{ User => UserModel }
|
||||
import lila.common.Template
|
||||
|
||||
final class Challenge(
|
||||
env: Env
|
||||
|
@ -202,7 +201,7 @@ final class Challenge(
|
|||
Action.async { req =>
|
||||
import cats.implicits._
|
||||
val scopes = List(OAuthScope.Challenge.Write)
|
||||
(get("token1", req) map AccessToken.Id.apply, get("token2", req) map AccessToken.Id.apply).mapN {
|
||||
(get("token1", req) map Bearer.apply, get("token2", req) map Bearer.apply).mapN {
|
||||
env.oAuth.server.authBoth(scopes)
|
||||
} ?? {
|
||||
_ flatMap {
|
||||
|
@ -373,7 +372,7 @@ final class Challenge(
|
|||
strToken: String
|
||||
)(managedBy: lila.user.User, message: Option[Template]) =
|
||||
env.security.api.oauthScoped(
|
||||
lila.oauth.AccessToken.Id(strToken),
|
||||
Bearer(strToken),
|
||||
List(lila.oauth.OAuthScope.Challenge.Write)
|
||||
) flatMap {
|
||||
_.fold(
|
||||
|
|
|
@ -84,7 +84,7 @@ final class OAuth(env: Env) extends LilaController(env) {
|
|||
Json
|
||||
.obj(
|
||||
"token_type" -> "Bearer",
|
||||
"access_token" -> token.id.value
|
||||
"access_token" -> token.id.secret
|
||||
)
|
||||
.add("expires_in" -> token.expires.map(_.getSeconds - nowSeconds))
|
||||
)
|
||||
|
@ -106,7 +106,7 @@ final class OAuth(env: Env) extends LilaController(env) {
|
|||
Json
|
||||
.obj(
|
||||
"token_type" -> "Bearer",
|
||||
"access_token" -> token.id.value,
|
||||
"access_token" -> token.id.secret,
|
||||
"refresh_token" -> s"invalid_for_bc_${lila.common.ThreadLocalRandom.nextString(17)}"
|
||||
)
|
||||
.add("expires_in" -> token.expires.map(_.getSeconds - nowSeconds))
|
||||
|
@ -121,7 +121,7 @@ final class OAuth(env: Env) extends LilaController(env) {
|
|||
def tokenRevoke =
|
||||
Scoped() { implicit req => _ =>
|
||||
HTTPRequest.bearer(req) ?? { token =>
|
||||
env.oAuth.tokenApi.revoke(AccessToken.Id(token)) map env.oAuth.server.deleteCached inject NoContent
|
||||
env.oAuth.tokenApi.revoke(token) map env.oAuth.server.deleteCached inject NoContent
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ object dgt {
|
|||
)
|
||||
|
||||
def play(token: AccessToken)(implicit ctx: Context) =
|
||||
layout("play", embedJsUnsafeLoadThen(s"""LichessDgt.playPage("${token.id.value}")"""))(
|
||||
layout("play", embedJsUnsafeLoadThen(s"""LichessDgt.playPage("${token.id.secret}")"""))(
|
||||
div(id := "dgt-play-zone")(pre(id := "dgt-play-zone-log")),
|
||||
div(cls := "dgt__play__help")(
|
||||
h2(iconTag("", "If a move is not detected")),
|
||||
|
|
|
@ -54,7 +54,7 @@ object index {
|
|||
br,
|
||||
"You won’t be able to see it again!"
|
||||
),
|
||||
code(token.id.value)
|
||||
code(token.id.secret)
|
||||
)
|
||||
)
|
||||
},
|
||||
|
|
|
@ -91,10 +91,10 @@ object HTTPRequest {
|
|||
def printClient(req: RequestHeader) =
|
||||
s"${ipAddress(req)} origin:${~origin(req)} referer:${~referer(req)} ua:${~userAgent(req)}"
|
||||
|
||||
def bearer(req: RequestHeader): Option[String] =
|
||||
def bearer(req: RequestHeader): Option[Bearer] =
|
||||
req.headers.get(HeaderNames.AUTHORIZATION).flatMap { authorization =>
|
||||
val prefix = "Bearer "
|
||||
authorization.startsWith(prefix) option authorization.stripPrefix(prefix)
|
||||
authorization.startsWith(prefix) option Bearer(authorization.stripPrefix(prefix))
|
||||
}
|
||||
|
||||
def isOAuth(req: RequestHeader) = bearer(req).isDefined
|
||||
|
|
|
@ -20,6 +20,14 @@ object AssetVersion {
|
|||
|
||||
case class IsMobile(value: Boolean) extends AnyVal with BooleanValue
|
||||
|
||||
case class Bearer(secret: String) extends AnyVal {
|
||||
override def toString = "Bearer(***)"
|
||||
}
|
||||
object Bearer {
|
||||
def random() = Bearer(s"lio_${SecureRandom.nextString(32)}")
|
||||
def randomPersonal() = Bearer(SecureRandom.nextString(16)) // TODO: prefix lip_, more entropy
|
||||
}
|
||||
|
||||
sealed trait IpAddress {
|
||||
def value: String
|
||||
override def toString = value
|
||||
|
|
|
@ -3,11 +3,11 @@ package lila.oauth
|
|||
import org.joda.time.DateTime
|
||||
import reactivemongo.api.bson._
|
||||
|
||||
import lila.common.SecureRandom
|
||||
import lila.common.{ Bearer, SecureRandom }
|
||||
import lila.user.User
|
||||
|
||||
case class AccessToken(
|
||||
id: AccessToken.Id,
|
||||
id: Bearer,
|
||||
publicId: BSONObjectID,
|
||||
userId: User.ID,
|
||||
createdAt: Option[DateTime] = None, // for personal access tokens
|
||||
|
@ -22,12 +22,6 @@ case class AccessToken(
|
|||
|
||||
object AccessToken {
|
||||
|
||||
case class Id(value: String) extends AnyVal
|
||||
object Id {
|
||||
def random() = Id(s"lio_${SecureRandom.nextString(32)}")
|
||||
def randomPersonal() = Id(SecureRandom.nextString(16)) // TODO: prefix lip_, more entropy
|
||||
}
|
||||
|
||||
case class ForAuth(userId: User.ID, scopes: List[OAuthScope])
|
||||
|
||||
object BSONFields {
|
||||
|
@ -52,7 +46,7 @@ object AccessToken {
|
|||
BSONFields.scopes -> true
|
||||
)
|
||||
|
||||
implicit private[oauth] val accessTokenIdHandler = stringAnyValHandler[Id](_.value, Id.apply)
|
||||
implicit private[oauth] val accessTokenIdHandler = stringAnyValHandler[Bearer](_.secret, Bearer.apply)
|
||||
|
||||
implicit val ForAuthBSONReader = new BSONDocumentReader[ForAuth] {
|
||||
def readDocument(doc: BSONDocument) =
|
||||
|
@ -68,7 +62,7 @@ object AccessToken {
|
|||
|
||||
def reads(r: BSON.Reader): AccessToken =
|
||||
AccessToken(
|
||||
id = r.get[Id](id),
|
||||
id = r.get[Bearer](id),
|
||||
publicId = r.get[BSONObjectID](publicId),
|
||||
userId = r str userId,
|
||||
createdAt = r.getO[DateTime](createdAt),
|
||||
|
|
|
@ -3,6 +3,7 @@ package lila.oauth
|
|||
import org.joda.time.DateTime
|
||||
import reactivemongo.api.bson._
|
||||
|
||||
import lila.common.Bearer
|
||||
import lila.db.dsl._
|
||||
import lila.user.User
|
||||
|
||||
|
@ -15,7 +16,7 @@ final class AccessTokenApi(colls: OauthColls)(implicit ec: scala.concurrent.Exec
|
|||
|
||||
def create(granted: AccessTokenRequest.Granted): Fu[AccessToken] = {
|
||||
val token = AccessToken(
|
||||
id = AccessToken.Id.random(),
|
||||
id = Bearer.random(),
|
||||
publicId = BSONObjectID.generate(),
|
||||
userId = granted.userId,
|
||||
createdAt = DateTime.now().some,
|
||||
|
@ -90,12 +91,12 @@ final class AccessTokenApi(colls: OauthColls)(implicit ec: scala.concurrent.Exec
|
|||
} yield AccessTokenApi.Client(origin, usedAt, scopes)
|
||||
}
|
||||
|
||||
def revoke(token: AccessToken.Id): Fu[AccessToken.Id] =
|
||||
def revoke(token: Bearer): Fu[Bearer] =
|
||||
colls.token {
|
||||
_.delete.one($doc(F.id -> token)).inject(token)
|
||||
}
|
||||
|
||||
def revokeByClientOrigin(clientOrigin: String, user: User): Fu[List[AccessToken.Id]] =
|
||||
def revokeByClientOrigin(clientOrigin: String, user: User): Fu[List[Bearer]] =
|
||||
colls.token { coll =>
|
||||
coll
|
||||
.find(
|
||||
|
@ -116,7 +117,7 @@ final class AccessTokenApi(colls: OauthColls)(implicit ec: scala.concurrent.Exec
|
|||
F.clientOrigin -> clientOrigin
|
||||
)
|
||||
)
|
||||
.inject(invalidate.flatMap(_.getAsOpt[AccessToken.Id](F.id)))
|
||||
.inject(invalidate.flatMap(_.getAsOpt[Bearer](F.id)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import play.api.data._
|
|||
import play.api.data.Forms._
|
||||
import reactivemongo.api.bson.BSONObjectID
|
||||
|
||||
import lila.common.Bearer
|
||||
import lila.common.Form.absoluteUrl
|
||||
|
||||
object OAuthForm {
|
||||
|
@ -29,7 +30,7 @@ object OAuthForm {
|
|||
) {
|
||||
def make(user: lila.user.User) =
|
||||
AccessToken(
|
||||
id = AccessToken.Id.randomPersonal(),
|
||||
id = Bearer.randomPersonal(),
|
||||
publicId = BSONObjectID.generate(),
|
||||
userId = user.id,
|
||||
createdAt = DateTime.now.some,
|
||||
|
|
|
@ -4,7 +4,7 @@ import org.joda.time.DateTime
|
|||
import play.api.mvc.{ RequestHeader, Result }
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.common.HTTPRequest
|
||||
import lila.common.{ Bearer, HTTPRequest }
|
||||
import lila.db.dsl._
|
||||
import lila.user.{ User, UserRepo }
|
||||
|
||||
|
@ -19,13 +19,13 @@ final class OAuthServer(
|
|||
import OAuthServer._
|
||||
|
||||
def auth(req: RequestHeader, scopes: List[OAuthScope]): Fu[AuthResult] =
|
||||
reqToTokenId(req).fold[Fu[AuthResult]](fufail(MissingAuthorizationHeader)) {
|
||||
HTTPRequest.bearer(req).fold[Fu[AuthResult]](fufail(MissingAuthorizationHeader)) {
|
||||
auth(_, scopes)
|
||||
} recover { case e: AuthError =>
|
||||
Left(e)
|
||||
}
|
||||
|
||||
def auth(tokenId: AccessToken.Id, scopes: List[OAuthScope]): Fu[AuthResult] =
|
||||
def auth(tokenId: Bearer, scopes: List[OAuthScope]): Fu[AuthResult] =
|
||||
accessTokenCache.get(tokenId) orFailWith NoSuchToken flatMap {
|
||||
case at if scopes.nonEmpty && !scopes.exists(at.scopes.contains) => fufail(MissingScope(at.scopes))
|
||||
case at =>
|
||||
|
@ -38,8 +38,8 @@ final class OAuthServer(
|
|||
}
|
||||
|
||||
def authBoth(scopes: List[OAuthScope])(
|
||||
token1: AccessToken.Id,
|
||||
token2: AccessToken.Id
|
||||
token1: Bearer,
|
||||
token2: Bearer
|
||||
): Fu[Either[AuthError, (User, User)]] = for {
|
||||
auth1 <- auth(token1, scopes)
|
||||
auth2 <- auth(token2, scopes)
|
||||
|
@ -49,19 +49,16 @@ final class OAuthServer(
|
|||
result <- if (user1.user is user2.user) Left(OneUserWithTwoTokens) else Right(user1.user -> user2.user)
|
||||
} yield result
|
||||
|
||||
def deleteCached(id: AccessToken.Id): Unit =
|
||||
def deleteCached(id: Bearer): Unit =
|
||||
accessTokenCache.put(id, fuccess(none))
|
||||
|
||||
private def reqToTokenId(req: RequestHeader): Option[AccessToken.Id] =
|
||||
HTTPRequest.bearer(req).map(AccessToken.Id.apply)
|
||||
|
||||
private val accessTokenCache =
|
||||
cacheApi[AccessToken.Id, Option[AccessToken.ForAuth]](32, "oauth.server.personal_access_token") {
|
||||
cacheApi[Bearer, Option[AccessToken.ForAuth]](32, "oauth.server.personal_access_token") {
|
||||
_.expireAfterWrite(5 minutes)
|
||||
.buildAsyncFuture(fetchAccessToken)
|
||||
}
|
||||
|
||||
private def fetchAccessToken(tokenId: AccessToken.Id): Fu[Option[AccessToken.ForAuth]] =
|
||||
private def fetchAccessToken(tokenId: Bearer): Fu[Option[AccessToken.ForAuth]] =
|
||||
colls.token {
|
||||
_.ext.findAndUpdate[AccessToken.ForAuth](
|
||||
selector = $doc(F.id -> tokenId),
|
||||
|
|
|
@ -11,7 +11,7 @@ import reactivemongo.api.ReadPreference
|
|||
import scala.annotation.nowarn
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.common.{ ApiVersion, EmailAddress, HTTPRequest, IpAddress, SecureRandom }
|
||||
import lila.common.{ ApiVersion, Bearer, EmailAddress, HTTPRequest, IpAddress, SecureRandom }
|
||||
import lila.db.BSON.BSONJodaDateTimeHandler
|
||||
import lila.db.dsl._
|
||||
import lila.oauth.{ AccessToken, OAuthScope, OAuthServer }
|
||||
|
@ -147,7 +147,7 @@ final class SecurityApi(
|
|||
private def stripRolesOfUser(user: User) = user.copy(roles = user.roles.filter(nonModRoles.contains))
|
||||
|
||||
def oauthScoped(
|
||||
tokenId: AccessToken.Id,
|
||||
tokenId: Bearer,
|
||||
scopes: List[lila.oauth.OAuthScope]
|
||||
): Fu[lila.oauth.OAuthServer.AuthResult] =
|
||||
tryOauthServer().flatMap {
|
||||
|
|
|
@ -9,13 +9,10 @@ import play.api.data.Forms._
|
|||
import play.api.libs.json.Json
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.game.Game
|
||||
import lila.game.IdGenerator
|
||||
import lila.oauth.AccessToken
|
||||
import lila.oauth.OAuthScope
|
||||
import lila.oauth.OAuthServer
|
||||
import lila.common.{ Bearer, Template }
|
||||
import lila.game.{ Game, IdGenerator }
|
||||
import lila.oauth.{ AccessToken, OAuthScope, OAuthServer }
|
||||
import lila.user.User
|
||||
import lila.common.Template
|
||||
|
||||
object SetupBulk {
|
||||
|
||||
|
@ -76,7 +73,7 @@ object SetupBulk {
|
|||
}(_ => None)
|
||||
)
|
||||
|
||||
private[setup] def extractTokenPairs(str: String): List[(AccessToken.Id, AccessToken.Id)] =
|
||||
private[setup] def extractTokenPairs(str: String): List[(Bearer, Bearer)] =
|
||||
str
|
||||
.split(',')
|
||||
.view
|
||||
|
@ -85,11 +82,11 @@ object SetupBulk {
|
|||
w.trim -> b.trim
|
||||
}
|
||||
.collect {
|
||||
case (w, b) if w.nonEmpty && b.nonEmpty => (AccessToken.Id(w), AccessToken.Id(b))
|
||||
case (w, b) if w.nonEmpty && b.nonEmpty => (Bearer(w), Bearer(b))
|
||||
}
|
||||
.toList
|
||||
|
||||
case class BadToken(token: AccessToken.Id, error: OAuthServer.AuthError)
|
||||
case class BadToken(token: Bearer, error: OAuthServer.AuthError)
|
||||
|
||||
case class ScheduledGame(id: Game.ID, white: User.ID, black: User.ID)
|
||||
|
||||
|
|
Loading…
Reference in New Issue