Migrate to http-enumerator

This commit is contained in:
Michael Snoyman 2010-09-24 09:00:37 +02:00
parent 8227bb17a9
commit 7ebf584f52
5 changed files with 71 additions and 38 deletions

View File

@ -1,11 +1,13 @@
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
module Web.Authenticate.Facebook where module Web.Authenticate.Facebook where
import Network.HTTP.Wget import Network.HTTP.Enumerator
import Data.List (intercalate) import Data.List (intercalate)
import Data.Object import Data.Object
import Data.Object.Json import Data.Object.Json
import Data.ByteString.Char8 (pack) import qualified Data.ByteString.Lazy.Char8 as L8
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString as S
data Facebook = Facebook data Facebook = Facebook
{ facebookClientId :: String { facebookClientId :: String
@ -43,8 +45,8 @@ accessTokenUrl fb code = concat
getAccessToken :: Facebook -> String -> IO AccessToken getAccessToken :: Facebook -> String -> IO AccessToken
getAccessToken fb code = do getAccessToken fb code = do
let url = accessTokenUrl fb code let url = accessTokenUrl fb code
b <- wget url [] [] b <- simpleHttp url
let (front, back) = splitAt 13 b let (front, back) = splitAt 13 $ L8.unpack b
case front of case front of
"access_token=" -> return $ AccessToken back "access_token=" -> return $ AccessToken back
_ -> error $ "Invalid facebook response: " ++ back _ -> error $ "Invalid facebook response: " ++ back
@ -60,5 +62,5 @@ graphUrl (AccessToken s) func = concat
getGraphData :: AccessToken -> String -> IO StringObject getGraphData :: AccessToken -> String -> IO StringObject
getGraphData at func = do getGraphData at func = do
let url = graphUrl at func let url = graphUrl at func
b <- wget url [] [] b <- simpleHttp url
decode $ pack b decode $ S.concat $ L.toChunks b

View File

@ -23,7 +23,7 @@ module Web.Authenticate.OpenId
, AuthenticateException (..) , AuthenticateException (..)
) where ) where
import Network.HTTP.Wget import Network.HTTP.Enumerator
import Text.HTML.TagSoup import Text.HTML.TagSoup
import Numeric (showHex) import Numeric (showHex)
import "transformers" Control.Monad.IO.Class import "transformers" Control.Monad.IO.Class
@ -31,6 +31,7 @@ import Data.Data
import Control.Failure hiding (Error) import Control.Failure hiding (Error)
import Control.Exception import Control.Exception
import Control.Monad (liftM) import Control.Monad (liftM)
import qualified Data.ByteString.Lazy.Char8 as L8
-- | An openid identifier (ie, a URL). -- | An openid identifier (ie, a URL).
newtype Identifier = Identifier { identifier :: String } newtype Identifier = Identifier { identifier :: String }
@ -44,12 +45,16 @@ instance Monad Error where
fail s = Error s fail s = Error s
-- | Returns a URL to forward the user to in order to login. -- | Returns a URL to forward the user to in order to login.
getForwardUrl :: (MonadIO m, Failure WgetException m) getForwardUrl :: (MonadIO m,
Failure InvalidUrlException m,
Failure HttpException m
)
=> String -- ^ The openid the user provided. => String -- ^ The openid the user provided.
-> String -- ^ The URL for this application\'s complete page. -> String -- ^ The URL for this application\'s complete page.
-> m String -- ^ URL to send the user to. -> m String -- ^ URL to send the user to.
getForwardUrl openid complete = do getForwardUrl openid complete = do
bodyIdent <- wget openid [] [] bodyIdent' <- simpleHttp openid
let bodyIdent = L8.unpack bodyIdent'
server <- getOpenIdVar "server" bodyIdent server <- getOpenIdVar "server" bodyIdent
let delegate = maybe openid id let delegate = maybe openid id
$ getOpenIdVar "delegate" bodyIdent $ getOpenIdVar "delegate" bodyIdent
@ -70,25 +75,28 @@ getOpenIdVar var content = do
mhead [] = fail $ "Variable not found: openid." ++ var -- FIXME mhead [] = fail $ "Variable not found: openid." ++ var -- FIXME
mhead (x:_) = return x mhead (x:_) = return x
constructUrl :: String -> [(String, String)] -> String constructUrl :: String -> [(String, String)] -> String -- FIXME no longer needed, use Request value directly
constructUrl url [] = url constructUrl url [] = url
constructUrl url args = url ++ "?" ++ queryString args constructUrl url args = url ++ "?" ++ queryString' args
where where
queryString [] = error "queryString with empty args cannot happen" queryString' [] = error "queryString with empty args cannot happen"
queryString [first] = onePair first queryString' [first] = onePair first
queryString (first:rest) = onePair first ++ "&" ++ queryString rest queryString' (first:rest) = onePair first ++ "&" ++ queryString' rest
onePair (x, y) = urlEncode x ++ "=" ++ urlEncode y onePair (x, y) = urlEncode x ++ "=" ++ urlEncode y
-- | Handle a redirect from an OpenID provider and check that the user -- | Handle a redirect from an OpenID provider and check that the user
-- logged in properly. If it was successfully, 'return's the openid. -- logged in properly. If it was successfully, 'return's the openid.
-- Otherwise, 'failure's an explanation. -- Otherwise, 'failure's an explanation.
authenticate :: (MonadIO m, Failure WgetException m, authenticate :: (MonadIO m,
Failure AuthenticateException m) Failure AuthenticateException m,
Failure InvalidUrlException m,
Failure HttpException m)
=> [(String, String)] => [(String, String)]
-> m Identifier -> m Identifier
authenticate req = do -- FIXME check openid.mode == id_res (not cancel) authenticate req = do -- FIXME check openid.mode == id_res (not cancel)
authUrl <- getAuthUrl req authUrl <- getAuthUrl req
content <- wget authUrl [] [] content' <- simpleHttp authUrl
let content = L8.unpack content'
let isValid = contains "is_valid:true" content let isValid = contains "is_valid:true" content
if isValid if isValid
then Identifier `liftM` alookup "openid.identity" req then Identifier `liftM` alookup "openid.identity" req
@ -99,7 +107,7 @@ alookup :: (Failure AuthenticateException m, Monad m)
-> [(String, String)] -> [(String, String)]
-> m String -> m String
alookup k x = case lookup k x of alookup k x = case lookup k x of
Just k -> return k Just k' -> return k'
Nothing -> failure $ MissingOpenIdParameter k Nothing -> failure $ MissingOpenIdParameter k
data AuthenticateException = AuthenticateException String data AuthenticateException = AuthenticateException String
@ -107,14 +115,14 @@ data AuthenticateException = AuthenticateException String
deriving (Show, Typeable) deriving (Show, Typeable)
instance Exception AuthenticateException instance Exception AuthenticateException
getAuthUrl :: (MonadIO m, getAuthUrl :: (MonadIO m, Failure AuthenticateException m,
Failure AuthenticateException m, Failure InvalidUrlException m,
Failure WgetException m) Failure HttpException m)
=> [(String, String)] -> m String => [(String, String)] -> m String
getAuthUrl req = do getAuthUrl req = do
identity <- alookup "openid.identity" req identity <- alookup "openid.identity" req
idContent <- wget identity [] [] idContent <- simpleHttp identity
helper idContent helper $ L8.unpack idContent
where where
helper idContent = do helper idContent = do
server <- getOpenIdVar "server" idContent server <- getOpenIdVar "server" idContent

View File

@ -1,6 +1,7 @@
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE PackageImports #-} {-# LANGUAGE PackageImports #-}
{-# LANGUAGE OverloadedStrings #-}
--------------------------------------------------------- ---------------------------------------------------------
-- --
-- Module : Web.Authenticate.Rpxnow -- Module : Web.Authenticate.Rpxnow
@ -21,13 +22,15 @@ module Web.Authenticate.Rpxnow
import Data.Object import Data.Object
import Data.Object.Json import Data.Object.Json
import Network.HTTP.Wget import Network.HTTP.Enumerator
import "transformers" Control.Monad.IO.Class import "transformers" Control.Monad.IO.Class
import Control.Failure import Control.Failure
import Data.Maybe import Data.Maybe
import Web.Authenticate.OpenId (AuthenticateException (..)) import Web.Authenticate.OpenId (AuthenticateException (..))
import Control.Monad import Control.Monad
import Data.ByteString.Char8 (pack) import qualified Data.ByteString.Char8 as S
import qualified Data.ByteString.Lazy.Char8 as L
import Control.Exception (throwIO)
-- | Information received from Rpxnow after a valid login. -- | Information received from Rpxnow after a valid login.
data Identifier = Identifier data Identifier = Identifier
@ -37,7 +40,8 @@ data Identifier = Identifier
-- | Attempt to log a user in. -- | Attempt to log a user in.
authenticate :: (MonadIO m, authenticate :: (MonadIO m,
Failure WgetException m, Failure HttpException m,
Failure InvalidUrlException m,
Failure AuthenticateException m, Failure AuthenticateException m,
Failure ObjectExtractError m, Failure ObjectExtractError m,
Failure JsonDecodeError m) Failure JsonDecodeError m)
@ -45,16 +49,34 @@ authenticate :: (MonadIO m,
-> String -- ^ Token passed by client. -> String -- ^ Token passed by client.
-> m Identifier -> m Identifier
authenticate apiKey token = do authenticate apiKey token = do
b <- wget "https://rpxnow.com/api/v2/auth_info" let body = L.fromChunks
[] [ "apiKey="
[ ("apiKey", apiKey) , S.pack apiKey
, ("token", token) , "&token="
] , S.pack token
o <- decode $ pack b ]
let req =
Request
{ method = "POST"
, secure = True
, host = "rpxnow.com"
, port = 443
, path = "api/v2/auth_info"
, queryString = []
, requestHeaders =
[ ("Content-Type", "application/x-www-form-urlencoded")
]
, requestBody = body
}
res <- httpLbsRedirect req
let b = responseBody res
unless (200 <= statusCode res && statusCode res < 300) $
liftIO $ throwIO $ HttpException (statusCode res) b
o <- decode $ S.concat $ L.toChunks b
m <- fromMapping o m <- fromMapping o
stat <- lookupScalar "stat" m stat <- lookupScalar "stat" m
unless (stat == "ok") $ failure $ AuthenticateException $ unless (stat == "ok") $ failure $ AuthenticateException $
"Rpxnow login not accepted: " ++ stat ++ "\n" ++ b "Rpxnow login not accepted: " ++ stat ++ "\n" ++ L.unpack b
parseProfile m parseProfile m
parseProfile :: (Monad m, Failure ObjectExtractError m) parseProfile :: (Monad m, Failure ObjectExtractError m)

View File

@ -1,5 +1,5 @@
name: authenticate name: authenticate
version: 0.6.3.2 version: 0.6.4
license: BSD3 license: BSD3
license-file: LICENSE license-file: LICENSE
author: Michael Snoyman <michael@snoyman.com> author: Michael Snoyman <michael@snoyman.com>
@ -17,7 +17,7 @@ library
build-depends: base >= 4 && < 5, build-depends: base >= 4 && < 5,
data-object >= 0.3.1 && < 0.4, data-object >= 0.3.1 && < 0.4,
data-object-json >= 0.3.1 && < 0.4, data-object-json >= 0.3.1 && < 0.4,
http-wget >= 0.6 && < 0.7, http-enumerator >= 0.1.1 && < 0.2,
tagsoup >= 0.6 && < 0.12, tagsoup >= 0.6 && < 0.12,
failure >= 0.0.0 && < 0.2, failure >= 0.0.0 && < 0.2,
transformers >= 0.1 && < 0.3, transformers >= 0.1 && < 0.3,

View File

@ -3,6 +3,7 @@ import Yesod
import Web.Authenticate.Facebook import Web.Authenticate.Facebook
import Data.Object import Data.Object
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Network.HTTP.Enumerator
data FB = FB Facebook data FB = FB Facebook
fb :: FB fb :: FB
@ -22,9 +23,9 @@ getRootR = do
getFacebookR = do getFacebookR = do
FB f <- getYesod FB f <- getYesod
code <- runFormGet $ required $ input "code" code <- runFormGet' $ stringInput "code"
at <- liftIO $ getAccessToken f code at <- liftIO $ getAccessToken f code
mreq <-runFormGet $ optional $ input "req" mreq <- runFormGet' $ maybeStringInput "req"
let req = fromMaybe "me" mreq let req = fromMaybe "me" mreq
so <- liftIO $ getGraphData at req so <- liftIO $ getGraphData at req
let so' = objToHamlet so let so' = objToHamlet so
@ -39,7 +40,7 @@ getFacebookR = do
^so'^ ^so'^
|] |]
main = toWaiApp fb >>= basicHandler 3000 main = withHttpEnumerator $ basicHandler 3000 fb
objToHamlet :: StringObject -> Hamlet url objToHamlet :: StringObject -> Hamlet url
objToHamlet (Scalar s) = [$hamlet|$string.s$|] objToHamlet (Scalar s) = [$hamlet|$string.s$|]