orePlayCommon/app/controllers/sugar/Requests.scala
package controllers.sugar
import scala.language.higherKinds
import java.time.Instant
import play.api.mvc.{Request, WrappedRequest}
import ore.models.api.ApiKey
import ore.models.project.Project
import ore.models.user.User
import models.viewhelper._
import ore.db.{Model, ModelService}
import ore.models.organization.Organization
import ore.permission.Permission
import ore.permission.scope.{GlobalScope, HasScope}
import ore.util.OreMDC
import util.syntax._
import cats.Applicative
import org.slf4j.MDC
/**
* Contains the custom WrappedRequests used by Ore.
*/
object Requests {
case class ApiAuthInfo(user: Option[Model[User]], key: Option[ApiKey], expires: Instant, globalPerms: Permission)
case class ApiRequest[A](apiInfo: ApiAuthInfo, request: Request[A]) extends WrappedRequest[A](request) with OreMDC {
def user: Option[Model[User]] = apiInfo.user
def globalPermissions: Permission = apiInfo.globalPerms
def permissionIn[B: HasScope, F[_]](b: B)(implicit service: ModelService[F], F: Applicative[F]): F[Permission] =
if (b.scope == GlobalScope) F.pure(apiInfo.globalPerms)
else apiInfo.key.fold(F.pure(globalPermissions))(_.permissionsIn(b))
override def logMessage(s: String): String = {
user.foreach(mdcPutUser)
s
}
override def afterLog(): Unit = mdcClear()
}
private def mdcPutUser(user: Model[User]): Unit = {
MDC.put("currentUserId", user.id.toString)
MDC.put("currentUserName", user.name)
}
private def mdcPutProject(project: Model[Project]): Unit = {
MDC.put("currentProjectId", project.id.toString)
MDC.put("currentProjectSlug", project.slug)
}
private def mdcPutOrg(orga: Model[Organization]): Unit = {
MDC.put("currentOrgaId", orga.id.toString)
MDC.put("currentOrgaName", orga.name)
}
private def mdcClear(): Unit = {
MDC.remove("currentUserId")
MDC.remove("currentUserName")
MDC.remove("currentProjectId")
MDC.remove("currentProjectSlug")
MDC.remove("currentOrgaId")
MDC.remove("currentOrgaName")
}
/**
* Base Request for Ore that holds all data needed for rendering the header
*/
sealed trait OreRequest[A] extends WrappedRequest[A] with OreMDC {
def headerData: HeaderData
def currentUser: Option[Model[User]] = headerData.currentUser
def hasUser: Boolean = headerData.currentUser.isDefined
override def afterLog(): Unit = mdcClear()
}
final class SimpleOreRequest[A](val headerData: HeaderData, val request: Request[A])
extends WrappedRequest[A](request)
with OreRequest[A] {
override def logMessage(s: String): String = {
currentUser.foreach(mdcPutUser)
s
}
}
/** Represents a Request with a [[User]] and subject */
sealed trait ScopedRequest[A] extends OreRequest[A] {
type Subject
def user: Model[User]
def subject: Subject
}
object ScopedRequest {
type Aux[A, Subject0] = ScopedRequest[A] { type Subject = Subject0 }
}
sealed trait UserScopedRequest[A] extends ScopedRequest[A] {
type Subject = Model[User]
def subject: Model[User] = user
}
object UserScopedRequest {
implicit def hasScope: HasScope[UserScopedRequest[_]] = (_: UserScopedRequest[_]) => GlobalScope
}
/**
* A request that hold the currently authenticated [[User]].
*
* @param user Authenticated user
* @param request Request to wrap
*/
final class AuthRequest[A](val user: Model[User], val headerData: HeaderData, request: Request[A])
extends WrappedRequest[A](request)
with OreRequest[A]
with UserScopedRequest[A] {
override def logMessage(s: String): String = {
mdcPutUser(user)
s
}
}
/**
* A request that holds a [[Project]].
*
* @param data Project data to hold
* @param scoped scoped Project data to hold
* @param request Request to wrap
*/
sealed class ProjectRequest[A](
val data: ProjectData,
val scoped: ScopedProjectData,
val headerData: HeaderData,
val request: Request[A]
) extends WrappedRequest[A](request)
with OreRequest[A] {
def project: Model[Project] = data.project
override def logMessage(s: String): String = {
currentUser.foreach(mdcPutUser)
mdcPutProject(project)
s
}
}
/**
* A request that holds a Project and a [[AuthRequest]].
*
* @param data Project data to hold
* @param scoped scoped Project data to hold
* @param request An [[AuthRequest]]
*/
final case class AuthedProjectRequest[A](
override val data: ProjectData,
override val scoped: ScopedProjectData,
override val headerData: HeaderData,
override val request: AuthRequest[A]
) extends ProjectRequest[A](data, scoped, headerData, request)
with ScopedRequest[A]
with OreRequest[A] {
type Subject = Model[Project]
override def user: Model[User] = request.user
override val subject: Model[Project] = this.data.project
}
object AuthedProjectRequest {
implicit def hasScope: HasScope[AuthedProjectRequest[_]] = HasScope.projectScope(_.subject.id.value)
}
/**
* A request that holds an [[Organization]].
*
* @param data Organization data to hold
* @param scoped scoped Organization data to hold
* @param request Request to wrap
*/
sealed class OrganizationRequest[A](
val data: OrganizationData,
val scoped: ScopedOrganizationData,
val headerData: HeaderData,
val request: Request[A]
) extends WrappedRequest[A](request)
with OreRequest[A] {
override def logMessage(s: String): String = {
currentUser.foreach(mdcPutUser)
mdcPutOrg(data.orga)
s
}
}
/**
* A request that holds an [[Organization]] and an [[AuthRequest]].
*
* @param data Organization data to hold
* @param scoped scoped Organization data to hold
* @param request Request to wrap
*/
final case class AuthedOrganizationRequest[A](
override val data: OrganizationData,
override val scoped: ScopedOrganizationData,
override val headerData: HeaderData,
override val request: AuthRequest[A]
) extends OrganizationRequest[A](data, scoped, headerData, request)
with ScopedRequest[A]
with OreRequest[A] {
type Subject = Model[Organization]
override def user: Model[User] = request.user
override val subject: Model[Organization] = this.data.orga
}
object AuthedOrganizationRequest {
implicit def hasScope: HasScope[AuthedOrganizationRequest[_]] = HasScope.orgScope(_.subject.id.value)
}
}