packages feed

authenticate 0.8.2.2 → 0.9.0

raw patch · 8 files changed

+208/−135 lines, 8 filesdep +aesondep +attoparsecdep +blaze-builderdep −data-objectdep −data-object-jsondep −waidep ~http-enumeratorPVP ok

version bump matches the API change (PVP)

Dependencies added: aeson, attoparsec, blaze-builder, case-insensitive, enumerator, http-types, text

Dependencies removed: data-object, data-object-json, wai, wai-extra

Dependency ranges changed: http-enumerator

API changes (from Hackage documentation)

- Web.Authenticate.Facebook: accessTokenUrl :: Facebook -> String -> String
- Web.Authenticate.Facebook: graphUrl :: AccessToken -> String -> String
+ Web.Authenticate.Facebook: instance Exception InvalidJsonException
+ Web.Authenticate.Facebook: instance Show InvalidJsonException
+ Web.Authenticate.Facebook: instance Typeable InvalidJsonException
- Web.Authenticate.Facebook: AccessToken :: String -> AccessToken
+ Web.Authenticate.Facebook: AccessToken :: Text -> AccessToken
- Web.Authenticate.Facebook: Facebook :: String -> String -> String -> Facebook
+ Web.Authenticate.Facebook: Facebook :: Text -> Text -> Text -> Facebook
- Web.Authenticate.Facebook: facebookClientId :: Facebook -> String
+ Web.Authenticate.Facebook: facebookClientId :: Facebook -> Text
- Web.Authenticate.Facebook: facebookClientSecret :: Facebook -> String
+ Web.Authenticate.Facebook: facebookClientSecret :: Facebook -> Text
- Web.Authenticate.Facebook: facebookRedirectUri :: Facebook -> String
+ Web.Authenticate.Facebook: facebookRedirectUri :: Facebook -> Text
- Web.Authenticate.Facebook: getAccessToken :: Facebook -> String -> IO AccessToken
+ Web.Authenticate.Facebook: getAccessToken :: Facebook -> Text -> IO AccessToken
- Web.Authenticate.Facebook: getForwardUrl :: Facebook -> [String] -> String
+ Web.Authenticate.Facebook: getForwardUrl :: Facebook -> [Text] -> Text
- Web.Authenticate.Facebook: getGraphData :: AccessToken -> String -> IO StringObject
+ Web.Authenticate.Facebook: getGraphData :: AccessToken -> Text -> IO (Either String Value)
- Web.Authenticate.Facebook: unAccessToken :: AccessToken -> String
+ Web.Authenticate.Facebook: unAccessToken :: AccessToken -> Text
- Web.Authenticate.OAuth: signOAuth :: OAuth -> Credential -> Request -> IO Request
+ Web.Authenticate.OAuth: signOAuth :: OAuth -> Credential -> Request IO -> IO (Request IO)
- Web.Authenticate.OpenId: Identifier :: String -> Identifier
+ Web.Authenticate.OpenId: Identifier :: Text -> Identifier
- Web.Authenticate.OpenId: authenticate :: (MonadIO m, Failure AuthenticateException m, Failure HttpException m) => [(String, String)] -> m (Identifier, [(String, String)])
+ Web.Authenticate.OpenId: authenticate :: (MonadIO m, Failure AuthenticateException m, Failure HttpException m) => [(Text, Text)] -> m (Identifier, [(Text, Text)])
- Web.Authenticate.OpenId: getForwardUrl :: (MonadIO m, Failure AuthenticateException m, Failure HttpException m) => String -> String -> Maybe String -> [(String, String)] -> m String
+ Web.Authenticate.OpenId: getForwardUrl :: (MonadIO m, Failure AuthenticateException m, Failure HttpException m) => Text -> Text -> Maybe Text -> [(Text, Text)] -> m Text
- Web.Authenticate.OpenId: identifier :: Identifier -> String
+ Web.Authenticate.OpenId: identifier :: Identifier -> Text
- Web.Authenticate.Rpxnow: Identifier :: String -> [(String, String)] -> Identifier
+ Web.Authenticate.Rpxnow: Identifier :: Text -> [(Text, Text)] -> Identifier
- Web.Authenticate.Rpxnow: authenticate :: (MonadIO m, Failure HttpException m, Failure AuthenticateException m, Failure ObjectExtractError m, Failure JsonDecodeError m) => String -> String -> m Identifier
+ Web.Authenticate.Rpxnow: authenticate :: (MonadIO m, Failure HttpException m, Failure AuthenticateException m) => String -> String -> m Identifier
- Web.Authenticate.Rpxnow: extraData :: Identifier -> [(String, String)]
+ Web.Authenticate.Rpxnow: extraData :: Identifier -> [(Text, Text)]
- Web.Authenticate.Rpxnow: identifier :: Identifier -> String
+ Web.Authenticate.Rpxnow: identifier :: Identifier -> Text

Files

OpenId2/Discovery.hs view
@@ -29,10 +29,11 @@ import qualified Data.ByteString.Lazy.UTF8 as BSLU import qualified Data.ByteString.Char8 as S8 import Control.Arrow (first, (***))-import Control.Monad.IO.Class (MonadIO)+import Control.Monad.IO.Class (MonadIO (liftIO)) import Control.Failure (Failure (failure)) import Control.Monad (mplus, liftM)-import Network.Wai (ciOriginal)+import qualified Data.CaseInsensitive as CI+import Data.Text (Text, pack, unpack)  data Discovery = Discovery1 String (Maybe String)                | Discovery2 Provider Identifier IdentType@@ -53,7 +54,7 @@             res2 <- discoverHTML ident             case res2 of                 Just x -> return x-                Nothing -> failure $ DiscoveryException i+                Nothing -> failure $ DiscoveryException $ unpack i  -- YADIS-Based Discovery ------------------------------------------------------- @@ -68,12 +69,12 @@               -> m (Maybe (Provider, Identifier, IdentType)) discoverYADIS _ _ 0 = failure TooManyRedirects discoverYADIS ident mb_loc redirects = do-    let uri = fromMaybe (identifier ident) mb_loc+    let uri = fromMaybe (unpack $ identifier ident) mb_loc     req <- parseUrl uri-    res <- httpLbs req+    res <- liftIO $ withManager $ httpLbs req     let mloc = fmap S8.unpack              $ lookup "x-xrds-location"-             $ map (first $ map toLower . S8.unpack . ciOriginal)+             $ map (first $ map toLower . S8.unpack . CI.original)              $ responseHeaders res     let mloc' = if mloc == mb_loc then Nothing else mloc     case statusCode res of@@ -95,7 +96,7 @@   where   isOpenId svc = do     let tys = serviceTypes svc-        localId = maybe ident Identifier $ listToMaybe $ serviceLocalIDs svc+        localId = maybe ident (Identifier . pack) $ listToMaybe $ serviceLocalIDs svc         f (x,y) | x `elem` tys = Just y                 | otherwise    = Nothing     (lid, itype) <- listToMaybe $ mapMaybe f@@ -117,7 +118,7 @@              => Identifier              -> m (Maybe Discovery) discoverHTML ident'@(Identifier ident) =-    (parseHTML ident' . BSLU.toString) `liftM` simpleHttp ident+    (parseHTML ident' . BSLU.toString) `liftM` simpleHttp (unpack ident)  -- | Parse out an OpenID endpoint and an actual identifier from an HTML -- document.@@ -135,7 +136,7 @@       return $ Discovery1 server delegate     resolve2 ls = do       prov <- lookup "openid2.provider" ls-      let lid = maybe ident Identifier $ lookup "openid2.local_id" ls+      let lid = maybe ident (Identifier . pack) $ lookup "openid2.local_id" ls       -- Based on OpenID 2.0 spec, section 7.3.3, HTML discovery can only       -- result in a claimed identifier.       return $ Discovery2 (Provider prov) lid ClaimedIdent
OpenId2/Normalization.hs view
@@ -26,12 +26,13 @@     ( uriToString, normalizeCase, normalizeEscape     , normalizePathSegments, parseURI, uriPath, uriScheme, uriFragment     )+import Data.Text (Text, pack, unpack) -normalize :: Failure AuthenticateException m => String -> m Identifier+normalize :: Failure AuthenticateException m => Text -> m Identifier normalize ident =     case normalizeIdentifier $ Identifier ident of         Just i -> return i-        Nothing -> failure $ NormalizationException ident+        Nothing -> failure $ NormalizationException $ unpack ident  -- | Normalize an identifier, discarding XRIs. normalizeIdentifier :: Identifier -> Maybe Identifier@@ -42,12 +43,13 @@ -- normalize an XRI. normalizeIdentifier' :: (String -> Maybe String) -> Identifier                      -> Maybe Identifier-normalizeIdentifier' xri (Identifier str)+normalizeIdentifier' xri (Identifier str')   | null str                  = Nothing-  | "xri://" `isPrefixOf` str = Identifier `fmap` xri str-  | head str `elem` "=@+$!"   = Identifier `fmap` xri str+  | "xri://" `isPrefixOf` str = (Identifier . pack) `fmap` xri str+  | head str `elem` "=@+$!"   = (Identifier . pack) `fmap` xri str   | otherwise = fmt `fmap` (url >>= norm)   where+    str = unpack str'     url = parseURI str <|> parseURI ("http://" ++ str)      norm uri = validScheme >> return u@@ -59,6 +61,7 @@               | otherwise          = uriPath uri      fmt u = Identifier+          $ pack           $ normalizePathSegments           $ normalizeEscape           $ normalizeCase
OpenId2/Types.hs view
@@ -21,12 +21,13 @@ import Data.Data (Data) import Data.Typeable (Typeable) import Web.Authenticate.Internal+import Data.Text (Text)  -- | An OpenID provider. newtype Provider = Provider { providerURI :: String } deriving (Eq,Show)  -- | A valid OpenID identifier.-newtype Identifier = Identifier { identifier :: String }+newtype Identifier = Identifier { identifier :: Text }     deriving (Eq, Ord, Show, Read, Data, Typeable)  data IdentType = OPIdent | ClaimedIdent
Web/Authenticate/Facebook.hs view
@@ -1,70 +1,92 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE DeriveDataTypeable #-}-module Web.Authenticate.Facebook where+{-# LANGUAGE OverloadedStrings #-}+module Web.Authenticate.Facebook+    ( Facebook (..)+    , AccessToken (..)+    , getForwardUrl+    , getAccessToken+    , getGraphData+    ) where  import Network.HTTP.Enumerator import Data.List (intercalate)-import Data.Object-import Data.Object.Json+import Data.Aeson import qualified Data.ByteString.Lazy.Char8 as L8-import qualified Data.ByteString.Lazy as L-import qualified Data.ByteString as S import Web.Authenticate.Internal (qsEncode) import Data.Data (Data) import Data.Typeable (Typeable)+import Control.Exception (Exception, throwIO)+import Data.Attoparsec.Lazy (parse, eitherResult)+import qualified Data.ByteString.Char8 as S8+import Data.Text (Text, pack)+import qualified Data.Text as T+import qualified Data.Text.Encoding as TE+import Blaze.ByteString.Builder (toByteString, copyByteString)+import Blaze.ByteString.Builder.Char.Utf8 (fromText)+import Network.HTTP.Types (renderQueryText)+import Data.Monoid (mappend)+import Data.ByteString (ByteString)  data Facebook = Facebook-    { facebookClientId :: String-    , facebookClientSecret :: String-    , facebookRedirectUri :: String+    { facebookClientId :: Text+    , facebookClientSecret :: Text+    , facebookRedirectUri :: Text     }     deriving (Show, Eq, Read, Ord, Data, Typeable) -newtype AccessToken = AccessToken { unAccessToken :: String }+newtype AccessToken = AccessToken { unAccessToken :: Text }     deriving (Show, Eq, Read, Ord, Data, Typeable) -getForwardUrl :: Facebook -> [String] -> String-getForwardUrl fb perms = concat-    [ "https://graph.facebook.com/oauth/authorize?client_id="-    , qsEncode $ facebookClientId fb-    , "&redirect_uri="-    , qsEncode $ facebookRedirectUri fb-    , if null perms-        then ""-        else "&scope=" ++ qsEncode (intercalate "," perms)-    ]+getForwardUrl :: Facebook -> [Text] -> Text+getForwardUrl fb perms =+    TE.decodeUtf8 $ toByteString $+    copyByteString "https://graph.facebook.com/oauth/authorize"+    `mappend`+    renderQueryText True+        ( ("client_id", Just $ facebookClientId fb)+        : ("redirect_uri", Just $ facebookRedirectUri fb)+        : if null perms+            then []+            else [("scope", Just $ T.intercalate "," perms)]) -accessTokenUrl :: Facebook -> String -> String-accessTokenUrl fb code = concat-    [ "https://graph.facebook.com/oauth/access_token?client_id="-    , qsEncode $ facebookClientId fb-    , "&redirect_uri="-    , qsEncode $ facebookRedirectUri fb-    , "&client_secret="-    , qsEncode $ facebookClientSecret fb-    , "&code="-    , qsEncode code-    ] -getAccessToken :: Facebook -> String -> IO AccessToken+accessTokenUrl :: Facebook -> Text -> ByteString+accessTokenUrl fb code =+    toByteString $+    copyByteString "https://graph.facebook.com/oauth/access_token"+    `mappend`+    renderQueryText True+        [ ("client_id", Just $ facebookClientId fb)+        , ("redirect_uri", Just $ facebookRedirectUri fb)+        , ("code", Just code)+        ]++getAccessToken :: Facebook -> Text -> IO AccessToken getAccessToken fb code = do     let url = accessTokenUrl fb code-    b <- simpleHttp url+    b <- simpleHttp $ S8.unpack url     let (front, back) = splitAt 13 $ L8.unpack b     case front of-        "access_token=" -> return $ AccessToken back+        "access_token=" -> return $ AccessToken $ T.pack back         _ -> error $ "Invalid facebook response: " ++ back -graphUrl :: AccessToken -> String -> String-graphUrl (AccessToken s) func = concat-    [ "https://graph.facebook.com/"-    , func-    , "?access_token="-    , s-    ]+graphUrl :: AccessToken -> Text -> ByteString+graphUrl (AccessToken s) func =+    toByteString $+    copyByteString "https://graph.facebook.com/"+    `mappend` fromText func+    `mappend` renderQueryText True [("access_token", Just s)] -getGraphData :: AccessToken -> String -> IO StringObject+getGraphData :: AccessToken -> Text -> IO (Either String Value) getGraphData at func = do     let url = graphUrl at func-    b <- simpleHttp url-    decode $ S.concat $ L.toChunks b+    b <- simpleHttp $ S8.unpack url+    return $ eitherResult $ parse json b++getGraphData' :: AccessToken -> Text -> IO Value+getGraphData' a b = getGraphData a b >>= either (throwIO . InvalidJsonException) return++data InvalidJsonException = InvalidJsonException String+    deriving (Show, Typeable)+instance Exception InvalidJsonException
Web/Authenticate/OAuth.hs view
@@ -20,7 +20,7 @@ import qualified Data.ByteString.Lazy.Char8 as BSL import Data.Maybe import Control.Applicative-import Network.Wai.Parse+import Network.HTTP.Types (parseSimpleQuery) import Control.Exception import Control.Monad import Data.List (sortBy)@@ -30,9 +30,14 @@ import Data.ByteString.Base64 import Data.Time import Numeric-import Network.Wai (ResponseHeader) import Codec.Crypto.RSA (rsassa_pkcs1_v1_5_sign, ha_SHA1, PrivateKey(..))-+import Network.HTTP.Types (Header)+import Control.Arrow (second)+import Blaze.ByteString.Builder (toByteString)+import Data.Enumerator (($$), run_, Stream (..), continue)+import Data.Monoid (mconcat)+import Control.Monad.IO.Class (MonadIO (liftIO))+import Data.IORef (newIORef, readIORef, atomicModifyIORef)  -- | Data type for OAuth client (consumer). data OAuth = OAuth { oauthServerName      :: String        -- ^ Service name@@ -85,10 +90,10 @@ getTemporaryCredential :: OAuth         -- ^ OAuth Application                        -> IO Credential -- ^ Temporary Credential (Request Token & Secret). getTemporaryCredential oa = do-  let req = fromJust $ parseUrl (oauthRequestUri oa)+  let req = fromJust $ parseUrl $ oauthRequestUri oa   req' <- signOAuth oa emptyCredential (req { method = "POST" }) -  rsp <- httpLbs req'-  let dic = parseQueryString . toStrict . responseBody $ rsp+  rsp <- withManager $ httpLbs req'+  let dic = parseSimpleQuery . toStrict . responseBody $ rsp   return $ Credential dic  -- | URL to obtain OAuth verifier.@@ -104,8 +109,8 @@                -> IO Credential -- ^ Token Credential (Access Token & Secret) getAccessToken oa cr = do   let req = (fromJust $ parseUrl $ oauthAccessTokenUri oa) { method = "POST" }-  rsp <- signOAuth oa cr req >>= httpLbs-  let dic = parseQueryString . toStrict . responseBody $ rsp+  rsp <- signOAuth oa cr req >>= withManager . httpLbs+  let dic = parseSimpleQuery . toStrict . responseBody $ rsp   return $ Credential dic  getTokenCredential = getAccessToken@@ -136,12 +141,12 @@ -- | Add OAuth headers & sign to 'Request'. signOAuth :: OAuth              -- ^ OAuth Application           -> Credential         -- ^ Credential-          -> Request            -- ^ Original Request-          -> IO Request         -- ^ Signed OAuth Request+          -> Request IO         -- ^ Original Request+          -> IO (Request IO)    -- ^ Signed OAuth Request signOAuth oa crd req = do   crd' <- addTimeStamp =<< addNonce crd   let tok = injectOAuthToCred oa crd'-      sign = genSign oa tok req+  sign <- genSign oa tok req   return $ addAuthHeader (insert "oauth_signature" sign tok) req  baseTime :: UTCTime@@ -171,19 +176,19 @@           , ("oauth_version", "1.0")           ] cred -genSign :: OAuth -> Credential -> Request -> BS.ByteString+genSign :: MonadIO m => OAuth -> Credential -> Request m -> m BS.ByteString genSign oa tok req =   case oauthSignatureMethod oa of-    HMACSHA1 ->-      let text = getBaseString tok req-          key  = BS.intercalate "&" $ map paramEncode [oauthConsumerSecret oa, tokenSecret tok]-      in encode $ toStrict $ bytestringDigest $ hmacSha1 (fromStrict key) text+    HMACSHA1 -> do+      text <- getBaseString tok req+      let key  = BS.intercalate "&" $ map paramEncode [oauthConsumerSecret oa, tokenSecret tok]+      return $ encode $ toStrict $ bytestringDigest $ hmacSha1 (fromStrict key) text     PLAINTEXT ->-      BS.intercalate "&" $ map paramEncode [oauthConsumerSecret oa, tokenSecret tok]+      return $ BS.intercalate "&" $ map paramEncode [oauthConsumerSecret oa, tokenSecret tok]     RSASHA1 pr ->-      encode $ toStrict $ rsassa_pkcs1_v1_5_sign ha_SHA1 pr (getBaseString tok req)+      liftM (encode . toStrict . rsassa_pkcs1_v1_5_sign ha_SHA1 pr) (getBaseString tok req) -addAuthHeader :: Credential -> Request -> Request+addAuthHeader :: Credential -> Request a -> Request a addAuthHeader (Credential cred) req =   req { requestHeaders = insertMap "Authorization" (renderAuthHeader cred) $ requestHeaders req } @@ -199,24 +204,44 @@                                oct = '%' : replicate (2 - length num) '0' ++ num                            in BS.pack oct -getBaseString :: Credential -> Request -> BSL.ByteString-getBaseString tok req =+getBaseString :: MonadIO m => Credential -> Request m -> m BSL.ByteString+getBaseString tok req = do   let bsMtd  = BS.map toUpper $ method req       isHttps = secure req       scheme = if isHttps then "https" else "http"       bsPort = if (isHttps && port req /= 443) || (not isHttps && port req /= 80)                  then ':' `BS.cons` BS.pack (show $ port req) else ""       bsURI = BS.concat [scheme, "://", host req, bsPort, path req]-      bsQuery = queryString req-      bsBodyQ = if isBodyFormEncoded $ requestHeaders req-                  then parseQueryString (toStrict $ requestBody req) else []-      bsAuthParams = filter ((`notElem`["oauth_signature","realm", "oauth_token_secret"]).fst) $ unCredential tok+      bsQuery = map (second $ fromMaybe "") $ queryString req+  bsBodyQ <- if isBodyFormEncoded $ requestHeaders req+                  then liftM parseSimpleQuery $ toLBS (requestBody req)+                  else return []+  let bsAuthParams = filter ((`notElem`["oauth_signature","realm", "oauth_token_secret"]).fst) $ unCredential tok       allParams = bsQuery++bsBodyQ++bsAuthParams       bsParams = BS.intercalate "&" $ map (\(a,b)->BS.concat[a,"=",b]) $ sortBy compareTuple                    $ map (\(a,b) -> (paramEncode a,paramEncode b)) allParams-  in BSL.intercalate "&" $ map (fromStrict.paramEncode) [bsMtd, bsURI, bsParams]+  -- FIXME it would be much better to use http-types functions here+  return $ BSL.intercalate "&" $ map (fromStrict.paramEncode) [bsMtd, bsURI, bsParams] -isBodyFormEncoded :: [(ResponseHeader, BS.ByteString)] -> Bool+toLBS :: MonadIO m => RequestBody m -> m BS.ByteString+toLBS (RequestBodyLBS l) = return $ toStrict l+toLBS (RequestBodyBS s) = return s+toLBS (RequestBodyBuilder _ b) = return $ toByteString b+toLBS (RequestBodyEnum _ enum) = do+    i <- liftIO $ newIORef id+    run_ $ enum $$ go i+    liftIO $ liftM (toByteString . mconcat . ($ [])) $ readIORef i+  where+    go i =+        continue go'+      where+        go' (Chunks []) = continue go'+        go' (Chunks x) = do+            liftIO (atomicModifyIORef i $ \y -> (y . (x ++), ()))+            continue go'+        go' EOF = return ()++isBodyFormEncoded :: [Header] -> Bool isBodyFormEncoded = maybe False (=="application/x-www-form-urlencoded") . lookup "Content-Type"  compareTuple :: (Ord a, Ord b) => (a, b) -> (a, b) -> Ordering
Web/Authenticate/OpenId.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} module Web.Authenticate.OpenId     ( getForwardUrl     , authenticate@@ -17,30 +18,33 @@ import qualified Data.ByteString.Lazy.UTF8 as BSLU import Network.HTTP.Enumerator     ( parseUrl, urlEncodedBody, responseBody, httpLbsRedirect-    , HttpException+    , HttpException, withManager     ) import Control.Arrow ((***)) import Data.List (unfoldr) import Data.Maybe (fromMaybe)+import Data.Text (Text, pack, unpack)+import Data.Text.Encoding (encodeUtf8)  getForwardUrl     :: ( MonadIO m        , Failure AuthenticateException m        , Failure HttpException m        )-    => String -- ^ The openid the user provided.-    -> String -- ^ The URL for this application\'s complete page.-    -> Maybe String -- ^ Optional realm-    -> [(String, String)] -- ^ Additional parameters to send to the OpenID provider. These can be useful for using extensions.-    -> m String -- ^ URL to send the user to.+    => Text -- ^ The openid the user provided.+    -> Text -- ^ The URL for this application\'s complete page.+    -> Maybe Text -- ^ Optional realm+    -> [(Text, Text)] -- ^ Additional parameters to send to the OpenID provider. These can be useful for using extensions.+    -> m Text -- ^ URL to send the user to. getForwardUrl openid' complete mrealm params = do     let realm = fromMaybe complete mrealm     disc <- normalize openid' >>= discover     case disc of         Discovery1 server mdelegate ->-            return $ qsUrl server+            return $ pack $ qsUrl server+                $ map (unpack *** unpack) -- FIXME                 $ ("openid.mode", "checkid_setup")-                : ("openid.identity", fromMaybe openid' mdelegate)+                : ("openid.identity", maybe openid' pack mdelegate)                 : ("openid.return_to", complete)                 : ("openid.realm", realm)                 : ("openid.trust_root", complete)@@ -50,22 +54,22 @@                     case itype of                         ClaimedIdent -> i                         OPIdent -> "http://specs.openid.net/auth/2.0/identifier_select"-            return $ qsUrl p+            return $ pack $ qsUrl p                 $ ("openid.ns", "http://specs.openid.net/auth/2.0")                 : ("openid.mode", "checkid_setup")-                : ("openid.claimed_id", i')-                : ("openid.identity", i')-                : ("openid.return_to", complete)-                : ("openid.realm", realm)-                : params+                : ("openid.claimed_id", unpack i')+                : ("openid.identity", unpack i')+                : ("openid.return_to", unpack complete)+                : ("openid.realm", unpack realm)+                : map (unpack *** unpack) params  authenticate     :: ( MonadIO m        , Failure AuthenticateException m        , Failure HttpException m        )-    => [(String, String)]-    -> m (Identifier, [(String, String)])+    => [(Text, Text)]+    -> m (Identifier, [(Text, Text)]) authenticate params = do     unless (lookup "openid.mode" params == Just "id_res")         $ failure $ case lookup "openid.mode" params of@@ -74,8 +78,8 @@                             | m == "error" ->                                 case lookup "openid.error" params of                                   Nothing -> AuthenticationException "An error occurred, but no error message was provided."-                                  (Just e) -> AuthenticationException e-                            | otherwise -> AuthenticationException $ "mode is " ++ m ++ " but we were expecting id_res."+                                  (Just e) -> AuthenticationException $ unpack e+                            | otherwise -> AuthenticationException $ "mode is " ++ unpack m ++ " but we were expecting id_res."     ident <- case lookup "openid.identity" params of                 Just i -> return i                 Nothing ->@@ -84,20 +88,21 @@     let endpoint = case disc of                     Discovery1 p _ -> p                     Discovery2 (Provider p) _ _ -> p-    let params' = map (BSU.fromString *** BSU.fromString)+    let params' = map (encodeUtf8 *** encodeUtf8)                 $ ("openid.mode", "check_authentication")                 : filter (\(k, _) -> k /= "openid.mode") params     req' <- parseUrl endpoint     let req = urlEncodedBody params' req'-    rsp <- httpLbsRedirect req-    let rps = parseDirectResponse $ BSLU.toString $ responseBody rsp+    rsp <- liftIO $ withManager $ httpLbsRedirect req+    let rps = parseDirectResponse $ pack $ BSLU.toString $ responseBody rsp -- FIXME     case lookup "is_valid" rps of         Just "true" -> return (Identifier ident, rps)         _ -> failure $ AuthenticationException "OpenID provider did not validate"  -- | Turn a response body into a list of parameters.-parseDirectResponse :: String -> [(String, String)]-parseDirectResponse  = unfoldr step+parseDirectResponse :: Text -> [(Text, Text)]+parseDirectResponse  =+    map (pack *** pack) . unfoldr step . unpack   where     step []  = Nothing     step str = case split (== '\n') str of
Web/Authenticate/Rpxnow.hs view
@@ -22,8 +22,7 @@     , AuthenticateException (..)     ) where -import Data.Object-import Data.Object.Json+import Data.Aeson import Network.HTTP.Enumerator import "transformers" Control.Monad.IO.Class import Control.Failure@@ -35,20 +34,22 @@ import Web.Authenticate.Internal import Data.Data (Data) import Data.Typeable (Typeable)+import Data.Attoparsec.Lazy (parse)+import qualified Data.Attoparsec.Lazy as AT+import Data.Text (Text)+import qualified Data.Aeson.Types  -- | Information received from Rpxnow after a valid login. data Identifier = Identifier-    { identifier :: String-    , extraData :: [(String, String)]+    { identifier :: Text+    , extraData :: [(Text, Text)]     }     deriving (Eq, Ord, Read, Show, Data, Typeable)  -- | Attempt to log a user in. authenticate :: (MonadIO m,                  Failure HttpException m,-                 Failure AuthenticateException m,-                 Failure ObjectExtractError m,-                 Failure JsonDecodeError m)+                 Failure AuthenticateException m)              => String -- ^ API key given by RPXNOW.              -> String -- ^ Token passed by client.              -> m Identifier@@ -70,27 +71,39 @@                 , requestHeaders =                     [ ("Content-Type", "application/x-www-form-urlencoded")                     ]-                , requestBody = body+                , requestBody = RequestBodyLBS body+                , checkCerts = const $ return True                 }-    res <- httpLbsRedirect req+    res <- liftIO $ withManager $ httpLbsRedirect req     let b = responseBody res     unless (200 <= statusCode res && statusCode res < 300) $         liftIO $ throwIO $ StatusCodeException (statusCode res) b-    o <- decode $ S.concat $ L.toChunks b-    m <- fromMapping o-    stat <- lookupScalar "stat" m-    unless (stat == "ok") $ failure $ RpxnowException $-        "Rpxnow login not accepted: " ++ stat ++ "\n" ++ L.unpack b-    parseProfile m+    o <- unResult $ parse json b+    --m <- fromMapping o+    let mstat = flip Data.Aeson.Types.parse o $ \v ->+                case v of+                    Object m -> m .: "stat"+                    _ -> mzero+    case mstat of+        Success "ok" -> return ()+        Success stat -> failure $ RpxnowException $+            "Rpxnow login not accepted: " ++ stat ++ "\n" ++ L.unpack b+        _ -> failure $ RpxnowException "Now stat value found on Rpxnow response"+    case Data.Aeson.Types.parse parseProfile o of+        Success x -> return x+        Error e -> failure $ RpxnowException $ "Unable to parse Rpxnow response: " ++ e -parseProfile :: (Monad m, Failure ObjectExtractError m)-             => [(String, StringObject)] -> m Identifier-parseProfile m = do-    profile <- lookupMapping "profile" m-    ident <- lookupScalar "identifier" profile+unResult :: Failure AuthenticateException m => AT.Result a -> m a+unResult = either (failure . RpxnowException) return . AT.eitherResult++parseProfile :: Value -> Data.Aeson.Types.Parser Identifier+parseProfile (Object m) = do+    profile <- m .: "profile"+    ident <- m .: "identifier"     let profile' = mapMaybe go profile     return $ Identifier ident profile'   where     go ("identifier", _) = Nothing-    go (k, Scalar v) = Just (k, v)+    go (k, String v) = Just (k, v)     go _ = Nothing+parseProfile _ = mzero
authenticate.cabal view
@@ -1,5 +1,5 @@ name:            authenticate-version:         0.8.2.2+version:         0.9.0 license:         BSD3 license-file:    LICENSE author:          Michael Snoyman <michael@snoyman.com>@@ -15,9 +15,8 @@  library     build-depends:   base >= 4 && < 5,-                     data-object >= 0.3.1 && < 0.4,-                     data-object-json >= 0.3.1 && < 0.4,-                     http-enumerator >= 0.3.0 && < 0.4,+                     aeson >= 0.3.1.1 && < 0.4,+                     http-enumerator >= 0.6 && < 0.7,                      tagsoup >= 0.6 && < 0.13,                      failure >= 0.0.0 && < 0.2,                      transformers >= 0.1 && < 0.3,@@ -25,13 +24,17 @@                      utf8-string >= 0.3 && < 0.4,                      network >= 2.2.1 && < 2.4,                      xml >= 1.3.7 && < 1.4,-                     wai >= 0.3 && < 0.4,+                     case-insensitive >= 0.2 && < 0.3,                      RSA >= 1.0 && < 1.1,                      time >= 1.1 && < 1.3,                      base64-bytestring >= 0.1 && < 0.2,                      SHA >= 1.4 && < 1.5,                      random >= 1.0 && < 1.1,-                     wai-extra >= 0.3 && < 0.4+                     text >= 0.5 && < 1.0,+                     http-types >= 0.6 && < 0.7,+                     enumerator >= 0.4.7 && < 0.5,+                     blaze-builder >= 0.2 && < 0.4,+                     attoparsec >= 0.8.5 && < 0.9     exposed-modules: Web.Authenticate.Rpxnow,                      Web.Authenticate.OpenId,                      Web.Authenticate.OpenId.Providers,