remove oauth app crud

pull/9327/head
Niklas Fiekas 2021-07-01 11:13:57 +02:00
parent 5d2f68911f
commit 86969b97e2
15 changed files with 6 additions and 337 deletions

View File

@ -123,7 +123,6 @@ final class LilaComponents(ctx: ApplicationLoader.Context) extends BuiltInCompon
lazy val gameMod: GameMod = wire[GameMod]
lazy val notifyC: Notify = wire[Notify]
lazy val oAuth: OAuth = wire[OAuth]
lazy val oAuthApp: OAuthApp = wire[OAuthApp]
lazy val oAuthToken: OAuthToken = wire[OAuthToken]
lazy val options: Options = wire[Options]
lazy val page: Page = wire[Page]

View File

@ -11,7 +11,7 @@ import views._
import lila.api.Context
import lila.common.HTTPRequest
import lila.app._
import lila.oauth.{ AccessToken, AccessTokenRequest, AuthorizationRequest, PersonalToken }
import lila.oauth.{ AccessToken, AccessTokenRequest, AuthorizationRequest }
final class OAuth(env: Env) extends LilaController(env) {
@ -38,7 +38,7 @@ final class OAuth(env: Env) extends LilaController(env) {
withPrompt { prompt =>
fuccess(ctx.me.fold(Redirect(routes.Auth.login.url, Map("referrer" -> List(ctx.req.uri)))) { me =>
Ok(
html.oAuth.app.authorize(prompt, me, s"${routes.OAuth.authorizeApply}?${ctx.req.rawQueryString}")
html.oAuth.authorize(prompt, me, s"${routes.OAuth.authorizeApply}?${ctx.req.rawQueryString}")
)
})
}

View File

@ -1,32 +0,0 @@
package controllers
import lila.app._
import lila.oauth.{ AccessToken, OAuthApp => App }
import views._
final class OAuthApp(env: Env) extends LilaController(env) {
private val appApi = env.oAuth.appApi
private val forms = env.oAuth.forms
def index =
Auth { implicit ctx => me =>
appApi.mine(me) flatMap { made =>
appApi.authorizedBy(me) map { used =>
Ok(html.oAuth.app.index(made, used))
}
}
}
def delete(id: String) =
Auth { _ => me =>
appApi.deleteBy(App.Id(id), me) inject
Redirect(s"${routes.OAuthApp.index}#made").flashSuccess
}
def revoke(id: String) =
Auth { _ => me =>
appApi.revoke(AccessToken.Id(id), me) inject
Redirect(routes.OAuthApp.index).flashSuccess
}
}

View File

@ -59,7 +59,6 @@ object layout {
a(activeCls("oauth.token"), href := routes.OAuthToken.index)(
"API access tokens"
),
ctx.noBot option a(activeCls("oauth.app"), href := routes.OAuthApp.index)("OAuth Apps"),
ctx.noBot option a(href := routes.DgtCtrl.index)("DGT board"),
div(cls := "sep"),
a(activeCls("close"), href := routes.Account.close)(

View File

@ -43,14 +43,6 @@ object security {
)
),
table(sessions, curSessionId.some, clients, personalAccessTokens)
),
div(cls := "account security box")(
h1("Additional third party apps"),
p(cls := "box__pad")(
"Revoke access of any ",
a(href := routes.OAuthApp.index)("third party apps"),
" that you do not trust."
)
)
)
}

View File

@ -1,106 +0,0 @@
package views.html.oAuth.app
import lila.api.Context
import lila.app.templating.Environment._
import lila.app.ui.ScalatagsTemplate._
import controllers.routes
object index {
def apply(made: List[lila.oauth.OAuthApp], used: List[lila.oauth.AccessToken.WithApp])(implicit
ctx: Context
) =
views.html.account.layout(title = "OAuth Apps", active = "oauth.app")(
div(cls := "account oauth")(
used.nonEmpty option div(cls := "oauth-used box")(
h1(id := "used")("OAuth Apps"),
standardFlash(cls := "box__pad"),
table(cls := "slist slist-pad")(
used.map { t =>
tr(
td(
strong(t.app.name),
" by ",
userIdLink(t.app.author.some),
br,
em(t.token.scopes.map(_.name).mkString(", "))
),
td(cls := "date")(
a(href := t.app.homepageUri.toString)(t.app.homepageUri.toString),
br,
t.token.usedAt map { at =>
frag("Last used ", momentFromNow(at))
}
),
td(cls := "action")(
postForm(action := routes.OAuthApp.revoke(t.token.id.value))(
submitButton(
cls := "button button-empty button-red confirm text",
title := s"Revoke access from ${t.app.name}",
dataIcon := ""
)("Revoke")
)
)
)
}
)
),
div(cls := "oauth-made box")(
h1(id := "made")("My OAuth Apps"),
p(cls := "box__pad")(
"Want to build something that integrates with and extends Lichess? ",
"Lichess now supports OAuth for unregistered and public clients with PKCE. ",
"Here's a ",
a(href := "https://github.com/lichess-org/api/tree/master/example/oauth-app")(
"Lichess OAuth app example"
),
", and the ",
a(href := routes.Api.index)("API documentation"),
".",
br,
br,
made.nonEmpty option {
frag(
flashMessage(cls := "flash-warning box__pad")(
"The following apps have been created while registration was still required. ",
"Please update them to use PKCE. ",
"Lichess will soon drop support for the authorization code flow without PKCE. ",
strong(a(href := "https://github.com/ornicar/lila/issues/9214")("More information")),
"."
),
br,
br
)
}
),
table(cls := "slist slist-pad")(
made.map { t =>
tr(
td(
strong(t.name),
br,
t.description.map { em(_) }
),
td(cls := "date")(
a(href := t.homepageUri.toString)(t.homepageUri.toString),
br,
"Created ",
momentFromNow(t.createdAt)
),
td(cls := "action")(
postForm(action := routes.OAuthApp.delete(t.clientId.value))(
submitButton(
cls := "button button-empty button-red confirm",
title := "Delete this app",
dataIcon := ""
)
)
)
)
}
)
)
)
)
}

View File

@ -1,6 +1,5 @@
package views.html
package oAuth
package app
import lila.api.Context
import lila.app.templating.Environment._

View File

@ -729,9 +729,6 @@ 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 /account/oauth/app controllers.OAuthApp.index
POST /account/oauth/app/:id/delete controllers.OAuthApp.delete(id: String)
POST /account/oauth/app/:id/revoke controllers.OAuthApp.revoke(id: String)
# Events
GET /event/$id<\w{8}> controllers.Event.show(id: String)

View File

@ -9,7 +9,6 @@ import lila.user.User
case class AccessToken(
id: AccessToken.Id,
publicId: BSONObjectID,
clientId: String,
userId: User.ID,
createdAt: Option[DateTime] = None, // for personal access tokens
description: Option[String] = None, // for personal access tokens
@ -26,17 +25,14 @@ 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: remove
def randomPersonal() = Id(SecureRandom.nextString(16)) // TODO: prefix lip_, more entropy
}
case class ForAuth(userId: User.ID, scopes: List[OAuthScope])
case class WithApp(token: AccessToken, app: OAuthApp)
object BSONFields {
val id = "access_token_id"
val publicId = "_id"
val clientId = "client_id"
val userId = "user_id"
val createdAt = "create_date"
val description = "description"
@ -74,7 +70,6 @@ object AccessToken {
AccessToken(
id = r.get[Id](id),
publicId = r.get[BSONObjectID](publicId),
clientId = r str clientId,
userId = r str userId,
createdAt = r.getO[DateTime](createdAt),
description = r strO description,
@ -88,7 +83,6 @@ object AccessToken {
$doc(
id -> o.id,
publicId -> o.publicId,
clientId -> o.clientId,
userId -> o.userId,
createdAt -> o.createdAt,
description -> o.description,

View File

@ -17,7 +17,6 @@ final class AccessTokenApi(colls: OauthColls)(implicit ec: scala.concurrent.Exec
val token = AccessToken(
id = AccessToken.Id.random(),
publicId = BSONObjectID.generate(),
clientId = PersonalToken.clientId, // TODO
userId = granted.userId,
createdAt = DateTime.now().some,
description = granted.redirectUri.clientOrigin.some,
@ -33,7 +32,6 @@ final class AccessTokenApi(colls: OauthColls)(implicit ec: scala.concurrent.Exec
_.find(
$doc(
F.userId -> user.id,
F.clientId -> PersonalToken.clientId,
F.clientOrigin -> $exists(false)
)
)
@ -47,7 +45,6 @@ final class AccessTokenApi(colls: OauthColls)(implicit ec: scala.concurrent.Exec
_.countSel(
$doc(
F.userId -> user.id,
F.clientId -> PersonalToken.clientId,
F.clientOrigin -> $exists(false)
)
)
@ -58,7 +55,6 @@ final class AccessTokenApi(colls: OauthColls)(implicit ec: scala.concurrent.Exec
_.one[AccessToken](
$doc(
F.userId -> user.id,
F.clientId -> PersonalToken.clientId,
F.clientOrigin -> $exists(false),
F.scopes $all scopes.toSeq
)
@ -141,8 +137,3 @@ object AccessTokenApi {
scopes: List[OAuthScope]
)
}
object PersonalToken {
val clientId = "lichess_personal_token"
}

View File

@ -11,8 +11,7 @@ import lila.db.AsyncColl
private case class OauthConfig(
@ConfigName("mongodb.uri") mongoUri: String,
@ConfigName("collection.access_token") tokenColl: CollName,
@ConfigName("collection.app") appColl: CollName
@ConfigName("collection.access_token") tokenColl: CollName
)
@Module
@ -31,9 +30,7 @@ final class Env(
private lazy val db = mongo.asyncDb("oauth", config.mongoUri)
private lazy val colls = new OauthColls(db(config.tokenColl), db(config.appColl))
lazy val appApi = wire[OAuthAppApi]
private lazy val colls = new OauthColls(db(config.tokenColl))
lazy val server = wire[OAuthServer]
@ -54,4 +51,4 @@ final class Env(
def forms = OAuthForm
}
private class OauthColls(val token: AsyncColl, val app: AsyncColl)
private class OauthColls(val token: AsyncColl)

View File

@ -1,76 +0,0 @@
package lila.oauth
import org.joda.time.DateTime
import lila.common.SecureRandom
import lila.user.User
import io.lemonlabs.uri.AbsoluteUrl
case class OAuthApp(
name: String,
clientId: OAuthApp.Id,
clientSecret: OAuthApp.Secret,
homepageUri: AbsoluteUrl,
redirectUri: AbsoluteUrl,
author: User.ID,
createdAt: DateTime,
description: Option[String] = None
)
object OAuthApp {
case class Id(value: String) extends AnyVal
case class Secret(value: String) extends AnyVal
def makeId = Id(SecureRandom nextString 16)
def makeSecret = Secret(SecureRandom nextString 32)
object BSONFields {
val clientId = "client_id"
val clientSecret = "client_secret"
val name = "name"
val homepageUri = "homepage_uri"
val redirectUri = "redirect_uri"
val author = "author"
val createdAt = "create_date"
val description = "description"
}
import reactivemongo.api.bson._
import lila.db.BSON
import lila.db.dsl._
import BSON.BSONJodaDateTimeHandler
implicit private[oauth] val AppIdHandler = stringAnyValHandler[Id](_.value, Id.apply)
implicit private[oauth] val AppSecretHandler = stringAnyValHandler[Secret](_.value, Secret.apply)
implicit val AppBSONHandler = new BSON[OAuthApp] {
import BSONFields._
def reads(r: BSON.Reader): OAuthApp =
OAuthApp(
clientId = r.get[Id](clientId),
clientSecret = r.get[Secret](clientSecret),
name = r str name,
homepageUri = r.get[AbsoluteUrl](homepageUri),
redirectUri =
r.get[List[AbsoluteUrl]](redirectUri).headOption err "Missing OAuthApp.redirectUri array",
author = r str author,
createdAt = r.get[DateTime](createdAt),
description = r strO description
)
def writes(w: BSON.Writer, o: OAuthApp) =
$doc(
clientId -> o.clientId,
clientSecret -> o.clientSecret,
name -> o.name,
homepageUri -> o.homepageUri,
redirectUri -> $arr(o.redirectUri),
author -> o.author,
createdAt -> o.createdAt,
description -> o.description
)
}
}

View File

@ -1,82 +0,0 @@
package lila.oauth
import lila.db.dsl._
import lila.user.User
final class OAuthAppApi(colls: OauthColls)(implicit ec: scala.concurrent.ExecutionContext) {
import OAuthApp.{ AppBSONHandler, AppIdHandler }
import OAuthApp.{ BSONFields => F }
def mine(u: User): Fu[List[OAuthApp]] =
colls.app {
_.find($doc(F.author -> u.id)).sort($sort desc F.createdAt).cursor[OAuthApp]().list(30)
}
def create(app: OAuthApp) = colls.app(_.insert.one(app).void)
def findBy(clientId: OAuthApp.Id, user: User): Fu[Option[OAuthApp]] =
colls.app {
_.one[OAuthApp](
$doc(
F.clientId -> clientId,
F.author -> user.id
)
)
}
def authorizedBy(user: User): Fu[List[AccessToken.WithApp]] =
colls.app { appColl =>
import OAuthApp.AppBSONHandler
colls.token {
_.aggregateList(maxDocs = 100) { implicit framework =>
import framework._
Match($doc("user_id" -> user.id)) -> List(
Sort(Descending("used_at")),
PipelineOperator(
$doc(
"$lookup" -> $doc(
"from" -> appColl.name,
"localField" -> "client_id",
"foreignField" -> "client_id",
"as" -> "app"
)
)
)
)
}.map { docs =>
for {
doc <- docs
token <- AccessToken.AccessTokenBSONHandler.readOpt(doc)
app <- doc.getAsOpt[List[OAuthApp]]("app").??(_.headOption)
} yield AccessToken.WithApp(token, app)
}
}
}
def revoke(id: AccessToken.Id, user: User): Funit =
colls.token {
_.delete.one($doc("access_token_id" -> id, "user_id" -> user.id)).void
}
def authorOf(clientId: OAuthApp.Id): Fu[Option[User.ID]] =
colls.app(_.primitiveOne[User.ID]($doc(F.clientId -> clientId), F.author))
def update(from: OAuthApp)(f: OAuthApp => OAuthApp): Fu[OAuthApp] = {
val app = f(from)
if (app == from) fuccess(app)
else colls.app(_.update.one($doc(F.clientId -> app.clientId), app)) inject app
}
def deleteBy(clientId: OAuthApp.Id, user: User) =
colls.app {
_.delete
.one(
$doc(
F.clientId -> clientId,
F.author -> user.id
)
)
.void
}
}

View File

@ -31,7 +31,6 @@ object OAuthForm {
AccessToken(
id = AccessToken.Id.randomPersonal(),
publicId = BSONObjectID.generate(),
clientId = PersonalToken.clientId,
userId = user.id,
createdAt = DateTime.now.some,
description = description.some,

View File

@ -1,7 +1,6 @@
package lila.oauth
import org.joda.time.DateTime
import play.api.http.HeaderNames.AUTHORIZATION
import play.api.mvc.{ RequestHeader, Result }
import scala.concurrent.duration._
@ -12,7 +11,6 @@ import lila.user.{ User, UserRepo }
final class OAuthServer(
colls: OauthColls,
userRepo: UserRepo,
appApi: OAuthAppApi,
cacheApi: lila.memo.CacheApi
)(implicit ec: scala.concurrent.ExecutionContext) {