packages feed

hoauth2 0.2.0 → 0.2.2

raw patch · 8 files changed

+164/−151 lines, 8 filesdep +bytestring-showdep +monad-controldep +transformersdep ~aesondep ~conduitdep ~http-conduitPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: bytestring-show, monad-control, transformers

Dependency ranges changed: aeson, conduit, http-conduit

API changes (from Hackage documentation)

- Network.OAuth2.HTTP.HttpClient: doRequest :: ResourceIO m => Request m -> m (Response ByteString)
- Network.OAuth2.HTTP.HttpClient: signRequest :: OAuth2 -> Request m -> Request m
+ Network.OAuth2.HTTP.HttpClient: doGetRequest :: MonadIO m => String -> [(ByteString, ByteString)] -> m (Response ByteString)
+ Network.OAuth2.HTTP.HttpClient: doPostRequst :: MonadIO m => String -> [(ByteString, ByteString)] -> m (Response ByteString)
+ Network.OAuth2.HTTP.HttpClient: doSimpleGetRequest :: MonadIO m => String -> m (Response ByteString)
+ Network.OAuth2.HTTP.HttpClient: requestAccessToken' :: OAuth2 -> ByteString -> IO ByteString
+ Network.OAuth2.OAuth2: accessTokenToParam :: ByteString -> [(ByteString, ByteString)]
+ Network.OAuth2.OAuth2: accessTokenUrl' :: OAuth2 -> ByteString -> Maybe ByteString -> (URI, PostBody)
+ Network.OAuth2.OAuth2: appendAccessToken :: URI -> OAuth2 -> URI
+ Network.OAuth2.OAuth2: appendQueryParam :: URI -> QueryParams -> URI
+ Network.OAuth2.OAuth2: transform' :: [(a, Maybe b)] -> [(a, b)]
+ Network.OAuth2.OAuth2: type PostBody = [(ByteString, ByteString)]
+ Network.OAuth2.OAuth2: type QueryParams = [(ByteString, ByteString)]
+ Network.OAuth2.OAuth2: type URI = ByteString

Files

README.md view
@@ -1,4 +1,8 @@-hoauth2-=======+## hoauth2  haskell oauth 2++## TODO++- To be a snaplet ?+- Chinese decode of response
hoauth2.cabal view
@@ -1,6 +1,6 @@ Name:                hoauth2 -- (http://www.haskell.org/haskellwiki/Package_versioning_policy)-Version:             0.2.0+Version:             0.2.2  Synopsis:            hoauth2 Description:         @@ -20,7 +20,7 @@ Copyright:           Haisheng,Wu Category:            Network Build-type:          Simple-stability:           experimental+stability:           alpha tested-with:         GHC == 7.0.3  @@ -28,8 +28,8 @@ -- a README. Extra-source-files:     README.md-  test/test-google.hs-  test/test-weibo.hs+  test/Google/test.hs+  test/Weibo/test.hs  Cabal-version:       >=1.6 @@ -37,6 +37,7 @@   Type:     git   Location: https://github.com/freizl/hoauth2 + Library   Ghc-Options:    -Wall     hs-source-dirs: src@@ -46,17 +47,21 @@    Build-Depends:     base          >= 4      && < 5,-    http-conduit  >= 1.2.5  && < 1.3.0,-    conduit       >= 0.2    && < 0.3.0,-    aeson         >= 0.4    && < 0.5,+    http-conduit  >= 1.4    && < 1.5,+    conduit       >= 0.4.1    && < 0.5,+    aeson         >= 0.6    && < 0.7,     mtl           >= 1      && < 2.2,     bytestring    >= 0.9    && < 0.10,-    http-types    >= 0.6.8  && < 0.7+    http-types    >= 0.6.8  && < 0.7,+    monad-control >= 0.3    && < 0.4,+    transformers  >= 0.2    && < 0.3,+    bytestring-show         >= 0.3.5    && < 0.4        -  -- Modules not exported by this package.-  -- Other-modules:       -  -  -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.-  -- Build-tools:         +  if impl(ghc >= 6.12.0)+      ghc-options: -threaded -Wall -fwarn-tabs -funbox-strict-fields -O2+                   -fno-warn-orphans -fno-warn-unused-do-bind+  else+      ghc-options: -threaded -Wall -fwarn-tabs -funbox-strict-fields -O2+                   -fno-warn-orphans           
src/Network/OAuth2/HTTP/HttpClient.hs view
@@ -1,34 +1,32 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}  {-   A simple OAuth2 http client. -} -module Network.OAuth2.HTTP.HttpClient -       ( requestAccessToken-       , doRequest-       , signRequest-       ) where+module Network.OAuth2.HTTP.HttpClient where -import Control.Monad.Trans.Resource+import Control.Applicative ((<$>))+import Control.Exception import Data.Aeson+import Network.HTTP.Conduit import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy.Char8 as BSL-import Data.List-import Data.Maybe-import Network.HTTP.Types (renderSimpleQuery, parseSimpleQuery) import qualified Network.HTTP.Types as HT-import Network.HTTP.Conduit-import Control.Exception-import Control.Applicative ((<$>))+import Control.Monad.Trans (liftIO)+import Control.Monad.IO.Class (MonadIO)+import Network.HTTP.Types (renderSimpleQuery)  import Network.OAuth2.OAuth2  --------------------------------------------------  -- | Request (POST method) access token URL in order to get @AccessToken@.+--  --   FIXME: what if @requestAccessToken'@ return error?+-- requestAccessToken :: OAuth2                  -> BS.ByteString          -- ^ Authentication code gained after authorization                 -> IO (Maybe AccessToken)@@ -36,30 +34,34 @@   requestAccessToken' :: OAuth2 -> BS.ByteString -> IO BSL.ByteString-requestAccessToken' oa code = doRequest req >>= retOrError+requestAccessToken' oa code = doPostRequst (BS.unpack uri) body >>= retOrError   where-    req = urlEncodedBody body $ toReq' uri     (uri, body) = accessTokenUrl oa code-    retOrError rsp = if (HT.statusCode . statusCode) rsp == 200+    retOrError rsp = if (HT.statusCode . responseStatus) rsp == 200+                        --then (print $ responseBody rsp ) >> (return $ responseBody rsp)                         then return $ responseBody rsp                         else throwIO . OAuthException $ "Gaining access_token failed: " ++ BSL.unpack (responseBody rsp)  --- | insert access token into the request-signRequest :: OAuth2 -> Request m -> Request m-signRequest oa req = req { queryString = renderSimpleQuery False newQuery }-  where-    newQuery = case oauthAccessToken oa of-                    Just at -> insert ("access_token", at) oldQuery-                    _ -> insert ("client_id", oauthClientId oa) . insert ("client_secret", oauthClientSecret oa) $ oldQuery-    oldQuery = parseSimpleQuery (queryString req)- ----------------------------------------------------- UTIL+-- od Request Utils -toReq' :: BS.ByteString -> Request a-toReq' = fromJust . parseUrl . BS.unpack+-- TODO: Some duplication here.+-- TODO: Control.Exception.try+--        result <- liftIO $ Control.Exception.try $ runResourceT $ httpLbs request man+-- +doSimpleGetRequest :: MonadIO m => String -> m (Response BSL.ByteString)+doSimpleGetRequest url = liftIO $ withManager $ \man -> do+    req' <- liftIO $ parseUrl url+    httpLbs req' man --- | Performance a http @Request@-doRequest :: ResourceIO m => Request m -> m (Response BSL.ByteString)-doRequest = withManager . httpLbs+doGetRequest :: MonadIO m => String -> [(BS.ByteString, BS.ByteString)] -> m (Response BSL.ByteString)+doGetRequest url pm = liftIO $ withManager $ \man -> do+    req' <- liftIO $ parseUrl $ url ++ BS.unpack (renderSimpleQuery True pm)+    httpLbs req' man++doPostRequst :: MonadIO m => String -> [(BS.ByteString, BS.ByteString)] -> m (Response BSL.ByteString)+doPostRequst url body = liftIO $ withManager $ \man -> do+    req' <- liftIO $ parseUrl url+    httpLbs (urlEncodedBody body req') man+
src/Network/OAuth2/OAuth2.hs view
@@ -6,23 +6,21 @@   This is sopposed to be independent with http client. -} -module Network.OAuth2.OAuth2-       ( OAuth2 (..)-       , AccessToken (..)-       , OAuthException (..)-       , authorizationUrl-       , accessTokenUrl-       ) where+module Network.OAuth2.OAuth2 where +import Control.Applicative ((<$>))+import Control.Exception+import Control.Monad (mzero) import Data.Aeson-import qualified Data.ByteString.Char8 as BS+import Data.Maybe (fromJust) import Data.Typeable (Typeable) import Network.HTTP.Types (renderSimpleQuery)-import Control.Exception-import Control.Applicative ((<$>))-import Control.Monad (mzero)+import qualified Data.ByteString as BS  -- | Query Parameter Representation+--+--   TODO: add a base endpoint URI.+--  data OAuth2 = OAuth2 { oauthClientId :: BS.ByteString                      , oauthClientSecret :: BS.ByteString                      , oauthOAuthorizeEndpoint :: BS.ByteString@@ -72,14 +70,13 @@  -- | Prepare the authorization URL. Redirect to this URL asking for user interactive authentication. authorizationUrl :: OAuth2 -> URI-authorizationUrl oa = (oauthOAuthorizeEndpoint oa) `appendQueryParam` queryStr+authorizationUrl oa = oauthOAuthorizeEndpoint oa `appendQueryParam` queryStr   where queryStr = transform' [ ("client_id", Just $ oauthClientId oa)                               , ("response_type", Just "code")                               , ("redirect_uri", oauthCallback oa)]   -- | Prepare access token URL and the request body query.- accessTokenUrl :: OAuth2                 -> BS.ByteString    -- ^ access code gained via authorization URL                -> (URI, PostBody)  -- ^ access token request URL plus the request body.@@ -97,4 +94,21 @@                           , ("code", Just code)                           , ("redirect_uri", oauthCallback oa)                           , ("grant_type", gt) ]+++--------------------------------------------------+-- UTIL++-- | For GET method API.+appendAccessToken :: URI   -- ^ Base URI+          -> OAuth2        -- ^ OAuth has Authorized Access Token+          -> URI           -- ^ Combined Result +appendAccessToken uri oauth = uri `BS.append` renderSimpleQuery True (accessTokenToParam $ token oauth)+                      where +                        -- Expect Access Token exists+                        token :: OAuth2 -> BS.ByteString+                        token = fromJust . oauthAccessToken++accessTokenToParam :: BS.ByteString -> [(BS.ByteString, BS.ByteString)]+accessTokenToParam token = [("access_token", token)] 
+ test/Google/test.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE OverloadedStrings #-}++{-+google web oauth: https://developers.google.com/accounts/docs/OAuth2WebServer+-}++module Main where++import qualified Data.ByteString.Char8 as BS+import Network.HTTP.Types (renderSimpleQuery, parseSimpleQuery)++import Network.OAuth2.HTTP.HttpClient+import Network.OAuth2.OAuth2+import Google.Key++gauth :: OAuth2+gauth = googleKeys { oauthOAuthorizeEndpoint = "https://accounts.google.com/o/oauth2/auth"+                   , oauthAccessTokenEndpoint = "https://accounts.google.com/o/oauth2/token" +                   , oauthAccessToken = Nothing+                   }++main :: IO ()+main = do +          print $ authorizationUrl gauth `BS.append` "&" `BS.append` googleScopeStr+          putStr "visit the url and paste code here: "+          code <- getLine+          token <- requestAccessToken gauth (BS.pack code) +          print token+++-- | this is special for google.+--googleScopeStr = renderSimpleQuery False [("scope", "https://www.googleapis.com/auth/userinfo.email")]
+ test/Weibo/test.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleContexts #-}++{-++weibo oauth2: http://open.weibo.com/wiki/Oauth2++This is very trivial testing of the httpclient api.+1. this case will print out a URL+2. run the URL in browser and will navigate to weibo auth page+3. conform the authentication and browser will navigate back to the callback url,+   which obviously will failed cause there is no local server.+4. copy the `code` in the callback url and parse into console+5. this test case will gain access token using the `code` and print it out.++check for integration testing at: +https://github.com/HaskellCNOrg/snaplet-oauth/tree/master/test++-}++module Main where++import qualified Data.ByteString.Char8 as BS+import Data.Maybe (fromJust)+import qualified Data.ByteString.Lazy.Char8 as BSL+import qualified Network.HTTP.Types as HT+import Network.HTTP.Conduit+import Control.Monad.Trans.Control (MonadBaseControl)+import Data.Conduit (MonadResource)++import Network.OAuth2.HTTP.HttpClient+import Network.OAuth2.OAuth2++import Weibo.Key++weibooauth :: OAuth2+weibooauth = weiboKey { oauthOAuthorizeEndpoint = "https://api.weibo.com/oauth2/authorize"+                      , oauthAccessTokenEndpoint = "https://api.weibo.com/oauth2/access_token" +                      , oauthAccessToken = Nothing+                      }+ +main :: IO ()+main = do +          print $ authorizationUrl weibooauth+          putStr "visit the url and paste code here: "+          code <- getLine+          token <- requestAccessToken weibooauth (BS.pack code)+          print token+
− test/test-google.hs
@@ -1,31 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--{--google web oauth: https://developers.google.com/accounts/docs/OAuth2WebServer--}--module Main where--import qualified Data.ByteString.Char8 as BS-import Network.HTTP.Types (renderSimpleQuery, parseSimpleQuery)--import Network.OAuth2.HTTP.HttpClient-import Network.OAuth2.OAuth2-import GoogleKey--gauth :: OAuth2-gauth = googleKeys { oauthOAuthorizeEndpoint = "https://accounts.google.com/o/oauth2/auth"-                   , oauthAccessTokenEndpoint = "https://accounts.google.com/o/oauth2/token" -                   , oauthAccessToken = Nothing-                   }--main :: IO ()-main = do -          print $ (authorizationUrl gauth) `BS.append` "&" `BS.append` googleScopeStr-          putStr "visit the url and paste code here: "-          code <- getLine-          token <- requestAccessToken gauth (BS.pack code) -          print token---- | this is special for google.-googleScopeStr = renderSimpleQuery False [("scope", "https://www.googleapis.com/auth/userinfo.email")]
− test/test-weibo.hs
@@ -1,62 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--{---weibo oauth2: http://open.weibo.com/wiki/Oauth2--This is very trivial testing of the httpclient api.-1. this case will print out a URL-2. run the URL in browser and will navigate to weibo auth page-3. conform the authentication and browser will navigate back to the callback url,-   which obviously will failed cause there is no local server.-4. copy the `code` in the callback url and parse into console-5. this test case will gain access token using the `code` and print it out.--TODO:-  1. a simple local server in order to make the test automatically.--}--module Main where--import qualified Data.ByteString.Char8 as BS-import Data.Maybe (fromJust)-import qualified Data.ByteString.Lazy.Char8 as BSL-import qualified Network.HTTP.Types as HT-import Network.HTTP.Conduit--import Network.OAuth2.HTTP.HttpClient-import Network.OAuth2.OAuth2--import WeiboKey--weibooauth :: OAuth2-weibooauth = weiboKey { oauthOAuthorizeEndpoint = "https://api.weibo.com/oauth2/authorize"-                      , oauthAccessTokenEndpoint = "https://api.weibo.com/oauth2/access_token" -                      , oauthAccessToken = Nothing-                      }--urlToRequest = fromJust . parseUrl--accountUidReq = urlToRequest "https://api.weibo.com/2/account/get_uid.json"---- | send a request and get a response body if successfully otherwise error-sendRequest :: Request IO -> IO BSL.ByteString-sendRequest req = do-    rsp <- doRequest req-    if (HT.statusCode . statusCode) rsp == 200-        then return $ responseBody rsp-        else return $ BSL.pack $ "Error when requesting: " ++ BSL.unpack (responseBody rsp)-  --- | fetch UID    -main :: IO ()-main = do -          print $ authorizationUrl weibooauth-          putStr "visit the url and paste code here: "-          code <- getLine-          token <- requestAccessToken weibooauth (BS.pack code)-          case token of -            Just (AccessToken token') -> do-                                         req <- return $ signRequest (weibooauth { oauthAccessToken = Just token'} ) accountUidReq-                                         print (queryString req)-                                         sendRequest req >>= print-            _ -> print "no token found"