scala-news-reader

rss/atom news reader in scala

git clone https://9o.is/git/scala-news-reader.git

OauthBuilder.scala

(4643B)


      1 package com.joereader.lib.api.rest.oauth
      2 
      3 import com.joereader.config._
      4 
      5 import dispatch._
      6 import Defaults._
      7 import com.ning.http.client._
      8 
      9 import net.liftweb._
     10 import common.{ Logger, Box, Full }
     11 import http._
     12 import util._
     13 import sitemap._, Loc._
     14 import json._
     15 
     16 import java.net.URL
     17 
     18 trait OauthBuilder extends Logger {
     19 
     20   /* Name to identify this oauth. */
     21   protected def name: String
     22 
     23   /* oauth server's sign in request url */
     24   protected def signInRequest: RequestBuilder
     25 
     26   /* oauth server's callback request url */
     27   protected def callbackRequest: RequestBuilder
     28 
     29   /* The access token. */
     30   protected def token: OauthAccessToken
     31 
     32   /* Called when oauth is done. */
     33   protected def endResponse(callback: URL): LiftResponse =
     34     DoRedirectResponse(callback.toString)
     35 
     36   /* 
     37    * if true, sends callback data in body encoded as 
     38    * application/x-www-form-urlencoded
     39    */
     40   protected def urlEncodedCallback = false
     41 
     42   /* oauth app key */
     43   private val key =
     44     Props.get(name + ".key") ?~ s"$name app key not found"
     45 
     46   /* oauth app secret */
     47   private val secret =
     48     Props.get(name + ".secret") ?~ s"$name app secret not found"
     49     
     50   /* Optional permissions. */
     51   protected def scope: List[String] = Nil
     52 
     53   /*
     54    * Full base url of our server. We need this as
     55    * the redirect uri.
     56    */
     57   protected val baseUrl =
     58     Props.get("oauth.baseurl") ?~ s"$name base url not found"
     59 
     60   /* 
     61    * Full callback url of our server. We need this as
     62    * the redirect uri.
     63    */
     64   private def callbackUrl =
     65     baseUrl.map(_ + myCallback.mkString("/"))
     66 
     67   /*
     68    * First step of oauth 2.0, send user to oauth server's
     69    * login screen.
     70    */
     71   private def signIn: Box[LiftResponse] =
     72     for {
     73       key <- key
     74       callback <- callbackUrl
     75     } yield {
     76       
     77       def permissions = 
     78         for(scope <- scope)
     79           yield "scope" -> scope      
     80       
     81       def req = signInRequest <<? Map(
     82         "client_id" -> key,
     83         "redirect_uri" -> callback,
     84         "scope" -> "email",
     85         "response_type" -> "code",
     86         "scope" -> scope.mkString(","))
     87 
     88       DoRedirectResponse(req.url)
     89     }
     90 
     91   /*
     92    * Second step of oauth 2.0, server sends us a code
     93    * and we trade that code for an access token.
     94    */
     95   private def callback: Box[LiftResponse] =
     96     for {
     97       key <- key
     98       secret <- secret
     99       callback <- callbackUrl
    100       code <- S.param("code") ?~ "Callback code not found"
    101     } yield {
    102       def params = Map(
    103         "client_id" -> key,
    104         "redirect_uri" -> callback,
    105         "client_secret" -> secret,
    106         "code" -> code,
    107         "grant_type" -> "authorization_code")
    108 
    109       def req =
    110         if (urlEncodedCallback) callbackRequest << params
    111         else callbackRequest <<? params
    112 
    113       Http(req OK as.String).either() match {
    114         case Left(e) =>
    115           error(e.getMessage)
    116           endResponse
    117         case Right(res) =>
    118           setAccessToken(res)
    119           endResponse
    120       }
    121 
    122     }
    123 
    124   /* 
    125    * Sets the oauth token. Override this if access 
    126    * token is not in body as json. 
    127    */
    128   protected def setAccessToken(res: String) {
    129     implicit val formats = DefaultFormats
    130     val value = parse(res).extract[AccessToken]
    131     token(Full(value))
    132     info(s"$name access token has been set to $value")
    133   }
    134 
    135   /* Called when oauth is done. */
    136   protected def endResponse: LiftResponse =
    137     (for {
    138       clientCallback <- ClientCallback.get
    139     } yield {
    140       endResponse(clientCallback)
    141     }) openOr {
    142       RedirectWithState(Site.home.url,
    143         RedirectState(() => {
    144           S.notice("Callback URL not found")
    145         }))
    146     }
    147 
    148   /* Our website's sign in url path. */
    149   private def mySignIn =
    150     "auth" :: name :: "signin" :: Nil
    151 
    152   /* Our website's callback url path. */
    153   private def myCallback =
    154     "auth" :: name :: "callback" :: Nil
    155 
    156   /* Sign In Menu item for sitemap. */
    157   def buildSignInMenu = Menu(Loc(
    158     name + " Sign In", mySignIn,
    159     name + ".signin", signInLocParams))
    160 
    161   /* Callback Menu item for sitemap. */
    162   def buildCallbackMenu = Menu(Loc(
    163     name + " Callback", myCallback,
    164     name + ".callback", callbackLocParams))
    165 
    166   /* 
    167    * Sets the client callback. 
    168    * Sets the referer by default.
    169    */
    170   protected def setClientCallback {
    171     ClientCallback(S.referer.map {
    172       r =>
    173         info(s"$name client callback set to $r")
    174         new java.net.URL(r)
    175     })
    176   }
    177 
    178   /* Response during sign in. Used by sitemap. */
    179   private def signInLocParams = EarlyResponse(() => {
    180     setClientCallback
    181     signIn
    182   }) :: Nil
    183 
    184   /* Response during callback. Used by sitemap. */
    185   private def callbackLocParams =
    186     EarlyResponse(() => { callback }) :: Nil
    187 }