refactor access token collection
parent
a367780a0a
commit
0aa632dd9e
|
@ -371,7 +371,7 @@ final class Challenge(
|
|||
challenge: lila.challenge.Challenge,
|
||||
strToken: String
|
||||
)(managedBy: lila.user.User, message: Option[Template]) =
|
||||
env.security.api.oauthScoped(
|
||||
env.oAuth.server.auth(
|
||||
Bearer(strToken),
|
||||
List(lila.oauth.OAuthScope.Challenge.Write)
|
||||
) flatMap {
|
||||
|
|
|
@ -84,7 +84,7 @@ final class OAuth(env: Env) extends LilaController(env) {
|
|||
Json
|
||||
.obj(
|
||||
"token_type" -> "Bearer",
|
||||
"access_token" -> token.id.secret
|
||||
"access_token" -> token.plain.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.secret,
|
||||
"access_token" -> token.plain.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(token) inject NoContent
|
||||
env.oAuth.tokenApi.revoke(AccessToken.Id.from(token)) inject NoContent
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package controllers
|
|||
import views._
|
||||
|
||||
import lila.app._
|
||||
import lila.oauth.AccessToken
|
||||
|
||||
final class OAuthToken(env: Env) extends LilaController(env) {
|
||||
|
||||
|
@ -38,8 +39,8 @@ final class OAuthToken(env: Env) extends LilaController(env) {
|
|||
)
|
||||
}
|
||||
|
||||
def delete(publicId: String) =
|
||||
Auth { _ => me =>
|
||||
tokenApi.revokeByPublicId(publicId, me) inject Redirect(routes.OAuthToken.index).flashSuccess
|
||||
def delete(id: String) =
|
||||
Auth { _ => _ =>
|
||||
tokenApi.revoke(AccessToken.Id(id)) inject Redirect(routes.OAuthToken.index).flashSuccess
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ object dgt {
|
|||
)
|
||||
|
||||
def play(token: AccessToken)(implicit ctx: Context) =
|
||||
layout("play", embedJsUnsafeLoadThen(s"""LichessDgt.playPage("${token.id.secret}")"""))(
|
||||
layout("play", embedJsUnsafeLoadThen(s"""LichessDgt.playPage("${token.plain.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.secret)
|
||||
code(token.plain.secret)
|
||||
)
|
||||
)
|
||||
},
|
||||
|
@ -75,7 +75,7 @@ object index {
|
|||
}
|
||||
),
|
||||
td(cls := "action")(
|
||||
postForm(action := routes.OAuthToken.delete(t.publicId.stringify))(
|
||||
postForm(action := routes.OAuthToken.delete(t.id.value))(
|
||||
submitButton(
|
||||
cls := "button button-red button-empty confirm",
|
||||
st.title := "Delete this access token"
|
||||
|
|
22
conf/routes
22
conf/routes
|
@ -718,17 +718,17 @@ GET /account/info controllers.Account.info
|
|||
GET /account/now-playing controllers.Account.nowPlaying
|
||||
|
||||
# OAuth
|
||||
GET /oauth controllers.OAuth.authorize
|
||||
POST /oauth controllers.OAuth.legacyTokenApply
|
||||
GET /oauth/authorize controllers.OAuth.legacyAuthorize
|
||||
POST /oauth/authorize controllers.OAuth.authorizeApply
|
||||
POST /oauth/revoke-client controllers.OAuth.revokeClient
|
||||
POST /api/token controllers.OAuth.tokenApply
|
||||
DELETE /api/token controllers.OAuth.tokenRevoke
|
||||
GET /account/oauth/token controllers.OAuthToken.index
|
||||
GET /account/oauth/token/create controllers.OAuthToken.create
|
||||
POST /account/oauth/token/create controllers.OAuthToken.createApply
|
||||
POST /account/oauth/token/:publicId/delete controllers.OAuthToken.delete(publicId: String)
|
||||
GET /oauth controllers.OAuth.authorize
|
||||
POST /oauth controllers.OAuth.legacyTokenApply
|
||||
GET /oauth/authorize controllers.OAuth.legacyAuthorize
|
||||
POST /oauth/authorize controllers.OAuth.authorizeApply
|
||||
POST /oauth/revoke-client controllers.OAuth.revokeClient
|
||||
POST /api/token controllers.OAuth.tokenApply
|
||||
DELETE /api/token controllers.OAuth.tokenRevoke
|
||||
GET /account/oauth/token controllers.OAuthToken.index
|
||||
GET /account/oauth/token/create controllers.OAuthToken.create
|
||||
POST /account/oauth/token/create controllers.OAuthToken.createApply
|
||||
POST /account/oauth/token/:id/delete controllers.OAuthToken.delete(id: String)
|
||||
|
||||
# Events
|
||||
GET /event/$id<\w{8}> controllers.Event.show(id: String)
|
||||
|
|
|
@ -2,16 +2,17 @@ package lila.oauth
|
|||
|
||||
import org.joda.time.DateTime
|
||||
import reactivemongo.api.bson._
|
||||
import com.roundeights.hasher.Algo
|
||||
|
||||
import lila.common.{ Bearer, SecureRandom }
|
||||
import lila.user.User
|
||||
|
||||
case class AccessToken(
|
||||
id: Bearer,
|
||||
publicId: BSONObjectID,
|
||||
id: AccessToken.Id,
|
||||
plain: Bearer,
|
||||
userId: User.ID,
|
||||
createdAt: Option[DateTime] = None, // for personal access tokens
|
||||
description: Option[String] = None, // for personal access tokens
|
||||
createdAt: Option[DateTime],
|
||||
description: Option[String], // for personal access tokens
|
||||
usedAt: Option[DateTime] = None,
|
||||
scopes: List[OAuthScope],
|
||||
clientOrigin: Option[String],
|
||||
|
@ -22,15 +23,20 @@ case class AccessToken(
|
|||
|
||||
object AccessToken {
|
||||
|
||||
case class Id(value: String) extends AnyVal
|
||||
object Id {
|
||||
def from(bearer: Bearer) = Id(Algo.sha256(bearer.secret).hex)
|
||||
}
|
||||
|
||||
case class ForAuth(userId: User.ID, scopes: List[OAuthScope])
|
||||
|
||||
object BSONFields {
|
||||
val id = "access_token_id"
|
||||
val publicId = "_id"
|
||||
val userId = "user_id"
|
||||
val createdAt = "create_date"
|
||||
val id = "_id"
|
||||
val plain = "plain"
|
||||
val userId = "userId"
|
||||
val createdAt = "created"
|
||||
val description = "description"
|
||||
val usedAt = "used_at"
|
||||
val usedAt = "used"
|
||||
val scopes = "scopes"
|
||||
val clientOrigin = "clientOrigin"
|
||||
val expires = "expires"
|
||||
|
@ -46,7 +52,8 @@ object AccessToken {
|
|||
BSONFields.scopes -> true
|
||||
)
|
||||
|
||||
implicit private[oauth] val accessTokenIdHandler = stringAnyValHandler[Bearer](_.secret, Bearer.apply)
|
||||
implicit private[oauth] val idHandler = stringAnyValHandler[Id](_.value, Id.apply)
|
||||
implicit private[oauth] val bearerHandler = stringAnyValHandler[Bearer](_.secret, Bearer.apply)
|
||||
|
||||
implicit val ForAuthBSONReader = new BSONDocumentReader[ForAuth] {
|
||||
def readDocument(doc: BSONDocument) =
|
||||
|
@ -62,8 +69,8 @@ object AccessToken {
|
|||
|
||||
def reads(r: BSON.Reader): AccessToken =
|
||||
AccessToken(
|
||||
id = r.get[Bearer](id),
|
||||
publicId = r.get[BSONObjectID](publicId),
|
||||
id = r.get[Id](id),
|
||||
plain = r.get[Bearer](plain),
|
||||
userId = r str userId,
|
||||
createdAt = r.getO[DateTime](createdAt),
|
||||
description = r strO description,
|
||||
|
@ -76,7 +83,7 @@ object AccessToken {
|
|||
def writes(w: BSON.Writer, o: AccessToken) =
|
||||
$doc(
|
||||
id -> o.id,
|
||||
publicId -> o.publicId,
|
||||
plain -> o.plain,
|
||||
userId -> o.userId,
|
||||
createdAt -> o.createdAt,
|
||||
description -> o.description,
|
||||
|
|
|
@ -8,14 +8,14 @@ import lila.common.Bearer
|
|||
import lila.db.dsl._
|
||||
import lila.user.{ User, UserRepo }
|
||||
|
||||
final class AccessTokenApi(colls: OauthColls, cacheApi: lila.memo.CacheApi, userRepo: UserRepo)(implicit
|
||||
final class AccessTokenApi(coll: Coll, cacheApi: lila.memo.CacheApi, userRepo: UserRepo)(implicit
|
||||
ec: scala.concurrent.ExecutionContext
|
||||
) {
|
||||
|
||||
import OAuthScope.scopeHandler
|
||||
import AccessToken.{ BSONFields => F, _ }
|
||||
import AccessToken.{ BSONFields => F }
|
||||
|
||||
def create(token: AccessToken): Funit = colls.token(_.insert.one(token).void)
|
||||
def create(token: AccessToken): Funit = coll.insert.one(token).void
|
||||
|
||||
def create(setup: OAuthForm.token.Data, me: User, isStudent: Boolean): Funit =
|
||||
(fuccess(isStudent) >>| userRepo.isManaged(me.id)) flatMap { noBot =>
|
||||
|
@ -28,12 +28,13 @@ final class AccessTokenApi(colls: OauthColls, cacheApi: lila.memo.CacheApi, user
|
|||
}
|
||||
|
||||
def create(granted: AccessTokenRequest.Granted): Fu[AccessToken] = {
|
||||
val plain = Bearer.random()
|
||||
val token = AccessToken(
|
||||
id = Bearer.random(),
|
||||
publicId = BSONObjectID.generate(),
|
||||
id = AccessToken.Id.from(plain),
|
||||
plain = plain,
|
||||
userId = granted.userId,
|
||||
description = None,
|
||||
createdAt = DateTime.now().some,
|
||||
description = granted.redirectUri.clientOrigin.some,
|
||||
scopes = granted.scopes,
|
||||
clientOrigin = granted.redirectUri.clientOrigin.some,
|
||||
expires = DateTime.now().plusMonths(12).some
|
||||
|
@ -42,127 +43,101 @@ final class AccessTokenApi(colls: OauthColls, cacheApi: lila.memo.CacheApi, user
|
|||
}
|
||||
|
||||
def listPersonal(user: User): Fu[List[AccessToken]] =
|
||||
colls.token {
|
||||
_.find(
|
||||
coll
|
||||
.find(
|
||||
$doc(
|
||||
F.userId -> user.id,
|
||||
F.clientOrigin -> $exists(false)
|
||||
)
|
||||
)
|
||||
.sort($sort desc F.createdAt)
|
||||
.cursor[AccessToken]()
|
||||
.list(100)
|
||||
}
|
||||
.sort($sort desc F.createdAt)
|
||||
.cursor[AccessToken]()
|
||||
.list(100)
|
||||
|
||||
def countPersonal(user: User): Fu[Int] =
|
||||
colls.token {
|
||||
_.countSel(
|
||||
$doc(
|
||||
F.userId -> user.id,
|
||||
F.clientOrigin -> $exists(false)
|
||||
)
|
||||
coll.countSel(
|
||||
$doc(
|
||||
F.userId -> user.id,
|
||||
F.clientOrigin -> $exists(false)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
def findCompatiblePersonal(user: User, scopes: Set[OAuthScope]): Fu[Option[AccessToken]] =
|
||||
colls.token {
|
||||
_.one[AccessToken](
|
||||
$doc(
|
||||
F.userId -> user.id,
|
||||
F.clientOrigin -> $exists(false),
|
||||
F.scopes $all scopes.toSeq
|
||||
)
|
||||
coll.one[AccessToken](
|
||||
$doc(
|
||||
F.userId -> user.id,
|
||||
F.clientOrigin -> $exists(false),
|
||||
F.scopes $all scopes.toSeq
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
def listClients(user: User, limit: Int): Fu[List[AccessTokenApi.Client]] =
|
||||
colls
|
||||
.token {
|
||||
_.aggregateList(limit) { framework =>
|
||||
import framework._
|
||||
Match(
|
||||
$doc(
|
||||
F.userId -> user.id,
|
||||
F.clientOrigin -> $exists(true)
|
||||
)
|
||||
) -> List(
|
||||
Unwind(path = F.scopes, includeArrayIndex = None, preserveNullAndEmptyArrays = Some(true)),
|
||||
GroupField(F.clientOrigin)(
|
||||
F.usedAt -> MaxField(F.usedAt),
|
||||
F.scopes -> AddFieldToSet(F.scopes)
|
||||
),
|
||||
Sort(Descending(F.usedAt))
|
||||
)
|
||||
}
|
||||
}
|
||||
.map { docs =>
|
||||
for {
|
||||
doc <- docs
|
||||
origin <- doc.getAsOpt[String]("_id")
|
||||
usedAt = doc.getAsOpt[DateTime](F.usedAt)
|
||||
scopes <- doc.getAsOpt[List[OAuthScope]](F.scopes)
|
||||
} yield AccessTokenApi.Client(origin, usedAt, scopes)
|
||||
}
|
||||
|
||||
def revoke(token: Bearer): Funit =
|
||||
colls.token {
|
||||
_.delete.one($doc(F.id -> token)).map(_ => invalidateCached(token))
|
||||
coll.aggregateList(limit) { framework =>
|
||||
import framework._
|
||||
Match(
|
||||
$doc(
|
||||
F.userId -> user.id,
|
||||
F.clientOrigin -> $exists(true)
|
||||
)
|
||||
) -> List(
|
||||
Unwind(path = F.scopes, includeArrayIndex = None, preserveNullAndEmptyArrays = Some(true)),
|
||||
GroupField(F.clientOrigin)(
|
||||
F.usedAt -> MaxField(F.usedAt),
|
||||
F.scopes -> AddFieldToSet(F.scopes)
|
||||
),
|
||||
Sort(Descending(F.usedAt))
|
||||
)
|
||||
} map { docs =>
|
||||
for {
|
||||
doc <- docs
|
||||
origin <- doc.getAsOpt[String]("_id")
|
||||
usedAt = doc.getAsOpt[DateTime](F.usedAt)
|
||||
scopes <- doc.getAsOpt[List[OAuthScope]](F.scopes)
|
||||
} yield AccessTokenApi.Client(origin, usedAt, scopes)
|
||||
}
|
||||
|
||||
def revoke(id: AccessToken.Id): Funit =
|
||||
coll.delete.one($id(id)).map(_ => invalidateCached(id))
|
||||
|
||||
def revokeByClientOrigin(clientOrigin: String, user: User): Funit =
|
||||
colls.token { coll =>
|
||||
coll
|
||||
.find(
|
||||
$doc(
|
||||
F.userId -> user.id,
|
||||
F.clientOrigin -> clientOrigin
|
||||
),
|
||||
$doc(F.id -> 1).some
|
||||
)
|
||||
.sort($sort desc F.usedAt)
|
||||
.cursor[Bdoc]()
|
||||
.list(100)
|
||||
.flatMap { invalidate =>
|
||||
coll.delete
|
||||
.one(
|
||||
$doc(
|
||||
F.userId -> user.id,
|
||||
F.clientOrigin -> clientOrigin
|
||||
)
|
||||
coll
|
||||
.find(
|
||||
$doc(
|
||||
F.userId -> user.id,
|
||||
F.clientOrigin -> clientOrigin
|
||||
),
|
||||
$doc(F.id -> 1).some
|
||||
)
|
||||
.sort($sort desc F.usedAt)
|
||||
.cursor[Bdoc]()
|
||||
.list(100)
|
||||
.flatMap { invalidate =>
|
||||
coll.delete
|
||||
.one(
|
||||
$doc(
|
||||
F.userId -> user.id,
|
||||
F.clientOrigin -> clientOrigin
|
||||
)
|
||||
.map(_ => invalidate.flatMap(_.getAsOpt[Bearer](F.id)).foreach(invalidateCached))
|
||||
}
|
||||
}
|
||||
|
||||
def revokeByPublicId(publicId: String, user: User): Funit =
|
||||
BSONObjectID.parse(publicId).toOption ?? { objectId =>
|
||||
colls.token { coll =>
|
||||
coll.findAndModify($doc(F.publicId -> objectId, F.userId -> user.id), coll.removeModifier) map {
|
||||
_.result[AccessToken].foreach { token =>
|
||||
invalidateCached(token.id)
|
||||
}
|
||||
}
|
||||
)
|
||||
.map(_ => invalidate.flatMap(_.getAsOpt[AccessToken.Id](F.id)).foreach(invalidateCached))
|
||||
}
|
||||
}
|
||||
|
||||
def get(bearer: Bearer) = accessTokenCache.get(bearer)
|
||||
def get(bearer: Bearer) = accessTokenCache.get(AccessToken.Id.from(bearer))
|
||||
|
||||
private val accessTokenCache =
|
||||
cacheApi[Bearer, Option[AccessToken.ForAuth]](32, "oauth.access_token") {
|
||||
cacheApi[AccessToken.Id, Option[AccessToken.ForAuth]](32, "oauth.access_token") {
|
||||
_.expireAfterWrite(5 minutes)
|
||||
.buildAsyncFuture(fetchAccessToken)
|
||||
}
|
||||
|
||||
private def fetchAccessToken(tokenId: Bearer): Fu[Option[AccessToken.ForAuth]] =
|
||||
colls.token {
|
||||
_.ext.findAndUpdate[AccessToken.ForAuth](
|
||||
selector = $doc(F.id -> tokenId),
|
||||
update = $set(F.usedAt -> DateTime.now()),
|
||||
fields = AccessToken.forAuthProjection.some
|
||||
)
|
||||
}
|
||||
private def fetchAccessToken(id: AccessToken.Id): Fu[Option[AccessToken.ForAuth]] =
|
||||
coll.ext.findAndUpdate[AccessToken.ForAuth](
|
||||
selector = $id(id),
|
||||
update = $set(F.usedAt -> DateTime.now()),
|
||||
fields = AccessToken.forAuthProjection.some
|
||||
)
|
||||
|
||||
private def invalidateCached(id: Bearer): Unit =
|
||||
private def invalidateCached(id: AccessToken.Id): Unit =
|
||||
accessTokenCache.put(id, fuccess(none))
|
||||
}
|
||||
|
||||
|
|
|
@ -2,53 +2,27 @@ package lila.oauth
|
|||
|
||||
import akka.actor._
|
||||
import com.softwaremill.macwire._
|
||||
import io.methvin.play.autoconfig._
|
||||
import play.api.Configuration
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.common.config._
|
||||
import lila.db.AsyncColl
|
||||
|
||||
private case class OauthConfig(
|
||||
@ConfigName("mongodb.uri") mongoUri: String,
|
||||
@ConfigName("collection.access_token") tokenColl: CollName
|
||||
)
|
||||
import lila.common.config.CollName
|
||||
|
||||
@Module
|
||||
final class Env(
|
||||
appConfig: Configuration,
|
||||
cacheApi: lila.memo.CacheApi,
|
||||
userRepo: lila.user.UserRepo,
|
||||
lilaDb: lila.db.Db,
|
||||
db: lila.db.Db,
|
||||
mongo: lila.db.Env
|
||||
)(implicit
|
||||
ec: scala.concurrent.ExecutionContext,
|
||||
system: ActorSystem
|
||||
) {
|
||||
|
||||
private val config = appConfig.get[OauthConfig]("oauth")(AutoConfig.loader)
|
||||
|
||||
private lazy val db = mongo.asyncDb("oauth", config.mongoUri)
|
||||
|
||||
private lazy val colls = new OauthColls(db(config.tokenColl))
|
||||
|
||||
lazy val server = wire[OAuthServer]
|
||||
|
||||
lazy val tryServer: OAuthServer.Try = () =>
|
||||
scala.concurrent
|
||||
.Future {
|
||||
server.some
|
||||
}
|
||||
.withTimeoutDefault(50 millis, none) recover { case e: Exception =>
|
||||
lila.log("security").warn("oauth", e)
|
||||
none
|
||||
}
|
||||
|
||||
lazy val tokenApi = wire[AccessTokenApi]
|
||||
lazy val authorizationApi = new AuthorizationApi(lilaDb(CollName("oauth2_authorization")))
|
||||
lazy val legacyClientApi = new LegacyClientApi(lilaDb(CollName("oauth2_legacy_client")))
|
||||
lazy val legacyClientApi = new LegacyClientApi(db(CollName("oauth2_legacy_client")))
|
||||
lazy val authorizationApi = new AuthorizationApi(db(CollName("oauth2_authorization")))
|
||||
lazy val tokenApi = new AccessTokenApi(db(CollName("oauth2_access_token")), cacheApi, userRepo)
|
||||
lazy val server = wire[OAuthServer]
|
||||
|
||||
def forms = OAuthForm
|
||||
}
|
||||
|
||||
private class OauthColls(val token: AsyncColl)
|
||||
|
|
|
@ -28,17 +28,19 @@ object OAuthForm {
|
|||
description: String,
|
||||
scopes: List[String]
|
||||
) {
|
||||
def make(user: lila.user.User) =
|
||||
def make(user: lila.user.User) = {
|
||||
val plain = Bearer.randomPersonal()
|
||||
AccessToken(
|
||||
id = Bearer.randomPersonal(),
|
||||
publicId = BSONObjectID.generate(),
|
||||
id = AccessToken.Id.from(plain),
|
||||
plain = plain,
|
||||
userId = user.id,
|
||||
createdAt = DateTime.now.some,
|
||||
createdAt = DateTime.now().some,
|
||||
description = description.some,
|
||||
scopes = scopes.flatMap(OAuthScope.byKey.get),
|
||||
clientOrigin = None,
|
||||
expires = None
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package lila.oauth
|
|||
|
||||
import org.joda.time.DateTime
|
||||
import play.api.mvc.{ RequestHeader, Result }
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.common.{ Bearer, HTTPRequest }
|
||||
import lila.db.dsl._
|
||||
|
@ -14,8 +13,6 @@ final class OAuthServer(
|
|||
cacheApi: lila.memo.CacheApi
|
||||
)(implicit ec: scala.concurrent.ExecutionContext) {
|
||||
|
||||
import AccessToken.accessTokenIdHandler
|
||||
import AccessToken.{ BSONFields => F }
|
||||
import OAuthServer._
|
||||
|
||||
def auth(req: RequestHeader, scopes: List[OAuthScope]): Fu[AuthResult] =
|
||||
|
@ -55,9 +52,7 @@ object OAuthServer {
|
|||
type AuthResult = Either[AuthError, OAuthScope.Scoped]
|
||||
|
||||
sealed abstract class AuthError(val message: String) extends lila.base.LilaException
|
||||
case object ServerOffline extends AuthError("OAuth server is offline! Try again soon.")
|
||||
case object MissingAuthorizationHeader extends AuthError("Missing authorization header")
|
||||
case object InvalidAuthorizationHeader extends AuthError("Invalid authorization header")
|
||||
case object NoSuchToken extends AuthError("No such token")
|
||||
case class MissingScope(scopes: List[OAuthScope]) extends AuthError("Missing scope")
|
||||
case object NoSuchUser extends AuthError("No such user")
|
||||
|
@ -70,6 +65,4 @@ object OAuthServer {
|
|||
"X-OAuth-Scopes" -> OAuthScope.keyList(availableScopes),
|
||||
"X-Accepted-OAuth-Scopes" -> OAuthScope.keyList(acceptedScopes)
|
||||
)
|
||||
|
||||
type Try = () => Fu[Option[OAuthServer]]
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ final class Env(
|
|||
noteApi: lila.user.NoteApi,
|
||||
cacheApi: lila.memo.CacheApi,
|
||||
settingStore: lila.memo.SettingStore.Builder,
|
||||
tryOAuthServer: OAuthServer.Try,
|
||||
oAuthServer: OAuthServer,
|
||||
mongoCache: lila.memo.MongoCache.Api,
|
||||
db: lila.db.Db
|
||||
)(implicit
|
||||
|
|
|
@ -26,7 +26,7 @@ final class SecurityApi(
|
|||
geoIP: GeoIP,
|
||||
authenticator: lila.user.Authenticator,
|
||||
emailValidator: EmailAddressValidator,
|
||||
tryOauthServer: lila.oauth.OAuthServer.Try,
|
||||
oAuthServer: lila.oauth.OAuthServer,
|
||||
tor: Tor
|
||||
)(implicit
|
||||
ec: scala.concurrent.ExecutionContext,
|
||||
|
@ -122,17 +122,9 @@ final class SecurityApi(
|
|||
|
||||
def oauthScoped(
|
||||
req: RequestHeader,
|
||||
scopes: List[lila.oauth.OAuthScope],
|
||||
retries: Int = 2
|
||||
scopes: List[lila.oauth.OAuthScope]
|
||||
): Fu[lila.oauth.OAuthServer.AuthResult] =
|
||||
tryOauthServer().flatMap {
|
||||
case None if retries > 0 =>
|
||||
lila.common.Future.delay(2 seconds) {
|
||||
oauthScoped(req, scopes, retries - 1)
|
||||
}
|
||||
case None => fuccess(Left(OAuthServer.ServerOffline))
|
||||
case Some(server) => server.auth(req, scopes) map { _ map stripRolesOfOAuthUser }
|
||||
}
|
||||
oAuthServer.auth(req, scopes) map { _ map stripRolesOfOAuthUser }
|
||||
|
||||
private lazy val nonModRoles: Set[String] = Permission.nonModPermissions.map(_.dbKey)
|
||||
|
||||
|
@ -146,15 +138,6 @@ final class SecurityApi(
|
|||
|
||||
private def stripRolesOfUser(user: User) = user.copy(roles = user.roles.filter(nonModRoles.contains))
|
||||
|
||||
def oauthScoped(
|
||||
tokenId: Bearer,
|
||||
scopes: List[lila.oauth.OAuthScope]
|
||||
): Fu[lila.oauth.OAuthServer.AuthResult] =
|
||||
tryOauthServer().flatMap {
|
||||
case None => fuccess(Left(OAuthServer.ServerOffline))
|
||||
case Some(server) => server.auth(tokenId, scopes)
|
||||
}
|
||||
|
||||
def locatedOpenSessions(userId: User.ID, nb: Int): Fu[List[LocatedSession]] =
|
||||
store.openSessions(userId, nb) map {
|
||||
_.map { session =>
|
||||
|
|
Loading…
Reference in New Issue