Switch to data-object-json
This commit is contained in:
parent
a8800d61d7
commit
4c00bd2058
@ -2,6 +2,7 @@
|
|||||||
{-# LANGUAGE DeriveDataTypeable #-}
|
{-# LANGUAGE DeriveDataTypeable #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# LANGUAGE PackageImports #-}
|
||||||
---------------------------------------------------------
|
---------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : Web.Authenticate.OpenId
|
-- Module : Web.Authenticate.OpenId
|
||||||
@ -19,20 +20,21 @@ module Web.Authenticate.OpenId
|
|||||||
( Identifier (..)
|
( Identifier (..)
|
||||||
, getForwardUrl
|
, getForwardUrl
|
||||||
, authenticate
|
, authenticate
|
||||||
|
, AuthenticateException (..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Network.HTTP.Wget
|
import Network.HTTP.Wget
|
||||||
import Text.HTML.TagSoup
|
import Text.HTML.TagSoup
|
||||||
import Numeric (showHex)
|
import Numeric (showHex)
|
||||||
import qualified Safe.Failure as A
|
#if MIN_VERSION_transformers(0,2,0)
|
||||||
#if TRANSFORMERS_02
|
import "transformers" Control.Monad.IO.Class
|
||||||
import Control.Monad.IO.Class
|
|
||||||
#else
|
#else
|
||||||
import Control.Monad.Trans
|
import "transformers" Control.Monad.Trans
|
||||||
#endif
|
#endif
|
||||||
import Data.Generics
|
import Data.Data
|
||||||
import Control.Failure hiding (Error)
|
import Control.Failure hiding (Error)
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
|
import Control.Monad (liftM)
|
||||||
|
|
||||||
-- | An openid identifier (ie, a URL).
|
-- | An openid identifier (ie, a URL).
|
||||||
newtype Identifier = Identifier { identifier :: String }
|
newtype Identifier = Identifier { identifier :: String }
|
||||||
@ -46,7 +48,7 @@ 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, MonadFailure WgetException m)
|
getForwardUrl :: (MonadIO m, Failure WgetException 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.
|
||||||
@ -84,9 +86,8 @@ constructUrl url args = url ++ "?" ++ queryString args
|
|||||||
-- | 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, MonadFailure WgetException m,
|
authenticate :: (MonadIO m, Failure WgetException m,
|
||||||
MonadFailure (A.LookupFailure String) m,
|
Failure AuthenticateException m)
|
||||||
MonadFailure AuthenticateException 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)
|
||||||
@ -94,23 +95,31 @@ authenticate req = do -- FIXME check openid.mode == id_res (not cancel)
|
|||||||
content <- wget authUrl [] []
|
content <- wget authUrl [] []
|
||||||
let isValid = contains "is_valid:true" content
|
let isValid = contains "is_valid:true" content
|
||||||
if isValid
|
if isValid
|
||||||
then A.lookup "openid.identity" req >>= return . Identifier
|
then Identifier `liftM` alookup "openid.identity" req
|
||||||
else failure $ AuthenticateException content
|
else failure $ AuthenticateException content
|
||||||
|
|
||||||
newtype AuthenticateException = AuthenticateException String
|
alookup :: (Failure AuthenticateException m, Monad m)
|
||||||
|
=> String
|
||||||
|
-> [(String, String)]
|
||||||
|
-> m String
|
||||||
|
alookup k x = case lookup k x of
|
||||||
|
Just k -> return k
|
||||||
|
Nothing -> failure $ MissingOpenIdParameter k
|
||||||
|
|
||||||
|
data AuthenticateException = AuthenticateException String
|
||||||
|
| MissingOpenIdParameter String
|
||||||
deriving (Show, Typeable)
|
deriving (Show, Typeable)
|
||||||
instance Exception AuthenticateException
|
instance Exception AuthenticateException
|
||||||
|
|
||||||
getAuthUrl :: (MonadIO m, MonadFailure (A.LookupFailure String) m,
|
getAuthUrl :: (MonadIO m,
|
||||||
MonadFailure WgetException m)
|
Failure AuthenticateException m,
|
||||||
|
Failure WgetException m)
|
||||||
=> [(String, String)] -> m String
|
=> [(String, String)] -> m String
|
||||||
getAuthUrl req = do
|
getAuthUrl req = do
|
||||||
identity <- A.lookup "openid.identity" req
|
identity <- alookup "openid.identity" req
|
||||||
idContent <- wget identity [] []
|
idContent <- wget identity [] []
|
||||||
helper idContent
|
helper idContent
|
||||||
where
|
where
|
||||||
helper :: MonadFailure (A.LookupFailure String) m
|
|
||||||
=> String -> m String
|
|
||||||
helper idContent = do
|
helper idContent = do
|
||||||
server <- getOpenIdVar "server" idContent
|
server <- getOpenIdVar "server" idContent
|
||||||
dargs <- mapM makeArg [
|
dargs <- mapM makeArg [
|
||||||
@ -122,11 +131,9 @@ getAuthUrl req = do
|
|||||||
]
|
]
|
||||||
let sargs = [("openid.mode", "check_authentication")]
|
let sargs = [("openid.mode", "check_authentication")]
|
||||||
return $ constructUrl server $ dargs ++ sargs
|
return $ constructUrl server $ dargs ++ sargs
|
||||||
makeArg :: MonadFailure (A.LookupFailure String) m
|
|
||||||
=> String -> m (String, String)
|
|
||||||
makeArg s = do
|
makeArg s = do
|
||||||
let k = "openid." ++ s
|
let k = "openid." ++ s
|
||||||
v <- A.lookup k req
|
v <- alookup k req
|
||||||
return (k, v)
|
return (k, v)
|
||||||
|
|
||||||
contains :: String -> String -> Bool
|
contains :: String -> String -> Bool
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# LANGUAGE PackageImports #-}
|
||||||
---------------------------------------------------------
|
---------------------------------------------------------
|
||||||
--
|
--
|
||||||
-- Module : Web.Authenticate.Rpxnow
|
-- Module : Web.Authenticate.Rpxnow
|
||||||
@ -18,15 +19,19 @@ module Web.Authenticate.Rpxnow
|
|||||||
, authenticate
|
, authenticate
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Text.JSON -- FIXME use Data.Object.JSON
|
import Data.Object
|
||||||
|
import Data.Object.Json
|
||||||
import Network.HTTP.Wget
|
import Network.HTTP.Wget
|
||||||
import Data.Maybe (isJust, fromJust)
|
#if MIN_VERSION_transformers(0,2,0)
|
||||||
#if TRANSFORMERS_02
|
import "transformers" Control.Monad.IO.Class
|
||||||
import Control.Monad.IO.Class
|
|
||||||
#else
|
#else
|
||||||
import Control.Monad.Trans
|
import "transformers" Control.Monad.Trans
|
||||||
#endif
|
#endif
|
||||||
import Control.Failure
|
import Control.Failure
|
||||||
|
import Data.Maybe
|
||||||
|
import Web.Authenticate.OpenId (AuthenticateException (..))
|
||||||
|
import Control.Monad
|
||||||
|
import Data.ByteString.Char8 (pack)
|
||||||
|
|
||||||
-- | Information received from Rpxnow after a valid login.
|
-- | Information received from Rpxnow after a valid login.
|
||||||
data Identifier = Identifier
|
data Identifier = Identifier
|
||||||
@ -35,43 +40,35 @@ data Identifier = Identifier
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- | Attempt to log a user in.
|
-- | Attempt to log a user in.
|
||||||
authenticate :: (MonadIO m, MonadFailure WgetException m, MonadFailure StringException m)
|
authenticate :: (MonadIO m,
|
||||||
|
Failure WgetException m,
|
||||||
|
Failure AuthenticateException m,
|
||||||
|
Failure ObjectExtractError m,
|
||||||
|
Failure JsonDecodeError m)
|
||||||
=> String -- ^ API key given by RPXNOW.
|
=> String -- ^ API key given by RPXNOW.
|
||||||
-> String -- ^ Token passed by client.
|
-> String -- ^ Token passed by client.
|
||||||
-> m Identifier
|
-> m Identifier
|
||||||
authenticate apiKey token = do
|
authenticate apiKey token = do
|
||||||
b <- wget
|
b <- wget "https://rpxnow.com/api/v2/auth_info"
|
||||||
"https://rpxnow.com/api/v2/auth_info"
|
[]
|
||||||
[]
|
[ ("apiKey", apiKey)
|
||||||
[ ("apiKey", apiKey)
|
, ("token", token)
|
||||||
, ("token", token)
|
]
|
||||||
]
|
o <- decode $ pack b
|
||||||
case decode b >>= getObject of
|
m <- fromMapping o
|
||||||
Error s -> failureString $ "Not a valid JSON response: " ++ s -- FIXME
|
stat <- lookupScalar "stat" m
|
||||||
Ok o ->
|
unless (stat == "ok") $ failure $ AuthenticateException $
|
||||||
case valFromObj "stat" o of
|
"Rpxnow login not accepted: " ++ stat ++ "\n" ++ b
|
||||||
Error _ -> failureString "Missing 'stat' field"
|
parseProfile m
|
||||||
Ok "ok" -> parseProfile o
|
|
||||||
Ok stat -> failureString $ "Login not accepted: " ++ stat
|
|
||||||
++ "\n" ++ b
|
|
||||||
|
|
||||||
parseProfile :: Monad m => JSObject JSValue -> m Identifier
|
parseProfile :: (Monad m, Failure ObjectExtractError m)
|
||||||
parseProfile v = do
|
=> [(String, StringObject)] -> m Identifier
|
||||||
profile <- resultToMonad $ valFromObj "profile" v >>= getObject
|
parseProfile m = do
|
||||||
ident <- resultToMonad $ valFromObj "identifier" profile
|
profile <- lookupMapping "profile" m
|
||||||
let pairs = fromJSObject profile
|
ident <- lookupScalar "identifier" profile
|
||||||
pairs' = filter (\(k, _) -> k /= "identifier") pairs
|
let profile' = mapMaybe go profile
|
||||||
pairs'' = map fromJust . filter isJust . map takeString $ pairs'
|
return $ Identifier ident profile'
|
||||||
return $ Identifier ident pairs''
|
where
|
||||||
|
go ("identifier", _) = Nothing
|
||||||
takeString :: (String, JSValue) -> Maybe (String, String)
|
go (k, Scalar v) = Just (k, v)
|
||||||
takeString (k, JSString v) = Just (k, fromJSString v)
|
go _ = Nothing
|
||||||
takeString _ = Nothing
|
|
||||||
|
|
||||||
getObject :: Monad m => JSValue -> m (JSObject JSValue)
|
|
||||||
getObject (JSObject o) = return o
|
|
||||||
getObject _ = fail "Not an object"
|
|
||||||
|
|
||||||
resultToMonad :: Monad m => Result a -> m a
|
|
||||||
resultToMonad (Ok x) = return x
|
|
||||||
resultToMonad (Error s) = fail s
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
name: authenticate
|
name: authenticate
|
||||||
version: 0.6.0.2
|
version: 0.6.2
|
||||||
license: BSD3
|
license: BSD3
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Michael Snoyman <michael@snoyman.com>
|
author: Michael Snoyman <michael@snoyman.com>
|
||||||
@ -13,22 +13,15 @@ cabal-version: >= 1.2
|
|||||||
build-type: Simple
|
build-type: Simple
|
||||||
homepage: http://github.com/snoyberg/authenticate/tree/master
|
homepage: http://github.com/snoyberg/authenticate/tree/master
|
||||||
|
|
||||||
flag transformers_02
|
|
||||||
description: transformers = 0.2.*
|
|
||||||
|
|
||||||
library
|
library
|
||||||
build-depends: base >= 4 && < 5,
|
build-depends: base >= 4 && < 5,
|
||||||
json >= 0.4.3 && < 0.5,
|
data-object >= 0.3.1 && < 0.4,
|
||||||
|
data-object-json >= 0.3.1 && < 0.4,
|
||||||
http-wget >= 0.6 && < 0.7,
|
http-wget >= 0.6 && < 0.7,
|
||||||
tagsoup >= 0.6 && < 0.10,
|
tagsoup >= 0.6 && < 0.10,
|
||||||
failure >= 0.0.0 && < 0.1,
|
failure >= 0.0.0 && < 0.2,
|
||||||
safe-failure >= 0.4 && < 0.5,
|
transformers >= 0.1 && < 0.3,
|
||||||
syb
|
bytestring >= 0.9 && < 0.10
|
||||||
if flag(transformers_02)
|
|
||||||
build-depends: transformers >= 0.2 && < 0.3
|
|
||||||
CPP-OPTIONS: -DTRANSFORMERS_02
|
|
||||||
else
|
|
||||||
build-depends: transformers >= 0.1 && < 0.2
|
|
||||||
exposed-modules: Web.Authenticate.Rpxnow,
|
exposed-modules: Web.Authenticate.Rpxnow,
|
||||||
Web.Authenticate.OpenId
|
Web.Authenticate.OpenId
|
||||||
ghc-options: -Wall -fno-warn-orphans
|
ghc-options: -Wall -fno-warn-orphans
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user