Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import akka.pattern.AskTimeoutException
- import com.ctilogic.cvd.acl.api.AccountsAPI.GetAccount
- import com.ctilogic.cvd.acl.api.{Account, PrivilegedRoleRequest, Session, Roles}
- import com.ctilogic.cvd.acl.api.SessionsAPI.{TouchSession, CheckSession}
- import com.ctilogic.cvd.common.api.EntityId
- import com.ctilogic.cvd.common.aux.StackTraceHelper
- import com.ctilogic.cvd.common.messaging._
- import com.github.levkhomich.akka.tracing.TracingSupport
- import reactivemongo.bson.BSONObjectID
- import scala.util.control.ControlThrowable
- // TODO: doc
- abstract class RestRequestProcessor[+T <: RestRequest : Manifest]
- extends RequestProcessor[T] with CommonAPIActor with DistributedServicesSupport
- {
- import context.become
- private var _sessionId: Option[EntityId] = None
- private var _sessionToken: Option[String] = None
- private var _accountId: Option[EntityId] = None
- private var _accountRoles: Set[Roles.Value] = Set.empty
- implicit protected val dispatcher = context.system.dispatchers.lookupHttpDispatcher
- def sessionId = _sessionId
- def sessionToken = _sessionToken
- def accountId = _accountId
- def accountRoles = _accountRoles
- override protected def stages: Stage = super.stages compose handleAuthorization
- /**
- * Provides backward compatibility for implicit rest response passing to the rootSender with errors in logs.
- */
- protected def passHttpResponseError: Receive = {
- case r: RestResponse ⇒
- log.error(s"Passing HTTP response $r from ${sender().path}; do not use it by default")
- complete(r)
- }
- override protected def handleDefaults = passHttpResponseError orElse super.handleDefaults
- protected def passHttpResponse: Receive = passHttpErrorResponse orElse passHttpCommonResponse
- protected def handleAuthorization(nextStage: Receive): Receive = {
- case r: RestRequest ⇒
- log.debug("Starting authorization")
- _sessionId = r.$sessionId.map(BSONObjectID.apply)
- _sessionToken = r.$sessionToken
- log.debug(s"SessionId: ${_sessionId}; SessionToken: ${_sessionToken}")
- log.debug(s"r.isInstanceOf[RequireAuthentication]: ${r.isInstanceOf[RequireAuthentication]}")
- if (r.isInstanceOf[RequireAuthentication] && (_sessionId.isEmpty || _sessionToken.isEmpty))
- complete(ErrorResponse.Unauthorized)
- else {
- stage(handleSession(nextStage))
- log.debug("Starting session processing")
- }
- }
- protected def handleSession(nextStage: Receive): Receive = {
- case r: RestRequest with RequireAuthentication ⇒
- tellService(NodeRole.ACL, CheckSession(sessionId.get, sessionToken.get))
- log.debug("Waiting for a Session(..)")
- become(waitSession(nextStage))
- case r: RestRequest ⇒
- log.debug("Session not required")
- stage(nextStage)
- log.debug("Authorization complete going to next stage")
- }
- protected def waitSession(nextStage: Receive): Receive = {
- case s: Session ⇒
- _accountId = Some(s.accountId)
- _accountRoles = s.roles
- log.debug("Session received accountId = ${_accountId}, roles = ${_accountRoles}")
- if (checkPrivilegedRequest(rootRequest)) {
- tellService(NodeRole.ACL, TouchSession(s.id))
- stage(nextStage)
- log.debug("Authorization complete going to next stage")
- }
- else
- complete(ErrorResponse.Forbidden)
- case ErrorResponse.NotFound ⇒
- complete(ErrorResponse.Unauthorized)
- }
- protected def checkPrivilegedRequest: (RestRequest ⇒ Boolean) = {
- case r: RestRequest with PrivilegedRoleRequest ⇒
- r.requiredRoles.subsetOf(accountRoles)
- case _ ⇒ true
- }
- protected def passHttpErrorResponse: Receive = {
- case e: ErrorResponse ⇒
- log.info(s"Passing HTTP error response $e from ${sender().path}")
- complete(e)
- }
- protected def passHttpCommonResponse: Receive = {
- case e: RestResponse ⇒
- log.info(s"Passing HTTP response $e from ${sender().path}")
- complete(e)
- }
- override def handleTimeout = handleTimeoutWith(complete(ErrorResponse.RequestTimeout))
- override def handleUnexpected = {
- case e ⇒
- log.error(s"Handled unexpected message ${e.getClass.getName} from ${sender().path}")
- complete(ErrorResponse.InternalServerError)
- }
- protected def tellService(role: NodeRole.Value, request: InternalRequest): Unit =
- tellService(NodeRole.ACL, request, rootRequest)
- }
- /**
- * This trait implements stackable pattern to provide Account object for the session handled by the request
- * processor.
- * This trait is private. The concrete implementations of this trait is [[HandleOptionAccount]], [[HandleAccount]].
- */
- trait BaseHandleAccount extends RestRequestProcessor[RestRequest] {
- import context.become
- protected var _account: Option[Account] = None
- abstract override protected def stages: Stage = super.stages compose handleAccount
- protected def handleAccount(nextStage: Receive): Receive = {
- case _ ⇒
- log.debug("Trying to obtain account")
- if (accountId.nonEmpty) {
- tellService(NodeRole.ACL, GetAccount(accountId.get))
- become {
- case a: Account ⇒ _account = Some(a)
- log.debug(s"Account received: $a; going to the next stage")
- stage(nextStage)
- case ErrorResponse.NotFound ⇒ onAccountNotFound(nextStage)
- }
- }
- else onAccountIdEmpty(nextStage)
- }
- protected def onAccountNotFound(nextStage: Receive): Unit = {
- log.debug("Account not found")
- log.debug("Going to the next stage")
- stage(nextStage)
- }
- protected def onAccountIdEmpty(nextStage: Receive): Unit = {
- log.debug("Unable to obtain account: accountId is empty")
- log.debug("Going to the next stage")
- stage(nextStage)
- }
- }
- /**
- * This trait implements stackable pattern to provide optional Account object for the session handled by the request
- * processor. The `account` will be None for request without session of session somehow not bound to any account.
- */
- trait HandleOptionAccount extends BaseHandleAccount {
- /**
- * The account of the current session.
- */
- def account = _account
- }
- /**
- * This trait implements stackable pattern to provide Account object for the session handled by the request
- * processor. If session is empty or if related account object not found. Processor will complete with
- * `ErrorResponse.InternalServerError`.
- */
- trait HandleAccount extends BaseHandleAccount {
- def account = _account.get
- override protected def onAccountIdEmpty(nextStage: Receive) = {
- log.error("Unable to get account. accountId is empty")
- complete(ErrorResponse.InternalServerError)
- }
- override protected def onAccountNotFound(nextStage: Receive) = {
- log.error("Unable to get account. Account not found.")
- complete(ErrorResponse.InternalServerError)
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement