diff --git a/Yesod/Helpers/Auth.hs b/Yesod/Helpers/Auth.hs index abc07621..cd88835d 100644 --- a/Yesod/Helpers/Auth.hs +++ b/Yesod/Helpers/Auth.hs @@ -4,6 +4,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE CPP #-} +{-# LANGUAGE OverloadedStrings #-} module Yesod.Helpers.Auth ( -- * Subsite Auth @@ -35,17 +36,22 @@ import qualified Data.ByteString.Char8 as S8 import qualified Network.Wai as W import Text.Hamlet (hamlet) import Data.Text.Lazy (pack) -import Data.JSON.Types (Value (..), Atom (AtomBoolean)) import qualified Data.Map as Map import Control.Monad.Trans.Class (lift) +import Data.Aeson +import Data.Text (Text) +import qualified Data.Text as T +import Data.Text.Encoding (decodeUtf8With) +import Data.Text.Encoding.Error (lenientDecode) +import Data.Monoid (mconcat) data Auth = Auth -type Method = String -type Piece = String +type Method = Text +type Piece = Text data AuthPlugin m = AuthPlugin - { apName :: String + { apName :: Text , apDispatch :: Method -> [Piece] -> GHandler Auth m () , apLogin :: forall s. (Route Auth -> Route m) -> GWidget s m () } @@ -55,9 +61,9 @@ getAuth = const Auth -- | User credentials data Creds m = Creds - { credsPlugin :: String -- ^ How the user was authenticated - , credsIdent :: String -- ^ Identifier. Exact meaning depends on plugin. - , credsExtra :: [(String, String)] + { credsPlugin :: Text -- ^ How the user was authenticated + , credsIdent :: Text -- ^ Identifier. Exact meaning depends on plugin. + , credsExtra :: [(Text, Text)] } class Yesod m => YesodAuth m where @@ -73,8 +79,8 @@ class Yesod m => YesodAuth m where getAuthId :: Creds m -> GHandler s m (Maybe (AuthId m)) - showAuthId :: m -> AuthId m -> String - readAuthId :: m -> String -> Maybe (AuthId m) + showAuthId :: m -> AuthId m -> Text + readAuthId :: m -> Text -> Maybe (AuthId m) authPlugins :: [AuthPlugin m] @@ -104,8 +110,9 @@ class Yesod m => YesodAuth m where messageEnterEmail _ = string "Enter your e-mail address below, and a confirmation e-mail will be sent to you." messageConfirmationEmailSentTitle :: m -> Html messageConfirmationEmailSentTitle _ = string "Confirmation e-mail sent" - messageConfirmationEmailSent :: m -> String -> Html - messageConfirmationEmailSent _ email = string $ "A confirmation e-mail has been sent to " ++ email ++ "." + messageConfirmationEmailSent :: m -> Text -> Html + messageConfirmationEmailSent _ email = toHtml $ mconcat + ["A confirmation e-mail has been sent to ", email, "."] messageAddressVerified :: m -> Html messageAddressVerified _ = string "Address verified, please set a new password" messageInvalidKeyTitle :: m -> Html @@ -132,10 +139,12 @@ class Yesod m => YesodAuth m where messageFacebook :: m -> Html messageFacebook _ = string "Login with Facebook" +type Texts = [Text] + mkYesodSub "Auth" [ ClassP ''YesodAuth [VarT $ mkName "master"] ] -#define STRINGS *Strings +#define STRINGS *Texts #if GHC7 [parseRoutes| #else @@ -144,10 +153,10 @@ mkYesodSub "Auth" /check CheckR GET /login LoginR GET /logout LogoutR GET POST -/page/#String/STRINGS PluginR +/page/#Text/STRINGS PluginR |] -credsKey :: String +credsKey :: Text credsKey = "_ID" -- | FIXME: won't show up till redirect @@ -202,10 +211,8 @@ $nothing
Not logged in. |] json creds = - ValueObject $ Map.fromList - [ (pack "logged_in" - , ValueAtom $ AtomBoolean - $ maybe False (const True) creds) + Object $ Map.fromList + [ (T.pack "logged_in", Bool $ maybe False (const True) creds) ] getLoginR :: YesodAuth m => GHandler Auth m RepHtml @@ -220,10 +227,10 @@ postLogoutR = do deleteSession credsKey redirectUltDest RedirectTemporary $ logoutDest y -handlePluginR :: YesodAuth m => String -> [String] -> GHandler Auth m () +handlePluginR :: YesodAuth m => Text -> [Text] -> GHandler Auth m () handlePluginR plugin pieces = do env <- waiRequest - let method = S8.unpack $ W.requestMethod env + let method = decodeUtf8With lenientDecode $ W.requestMethod env case filter (\x -> apName x == plugin) authPlugins of [] -> notFound ap:_ -> apDispatch ap method pieces diff --git a/Yesod/Helpers/Auth/Dummy.hs b/Yesod/Helpers/Auth/Dummy.hs index 62694026..e5125932 100644 --- a/Yesod/Helpers/Auth/Dummy.hs +++ b/Yesod/Helpers/Auth/Dummy.hs @@ -1,5 +1,6 @@ {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE CPP #-} +{-# LANGUAGE OverloadedStrings #-} -- | Provides a dummy authentication module that simply lets a user specify -- his/her identifier. This is not intended for real world use, just for -- testing. diff --git a/Yesod/Helpers/Auth/Email.hs b/Yesod/Helpers/Auth/Email.hs index 23dbdcf7..dd8508d1 100644 --- a/Yesod/Helpers/Auth/Email.hs +++ b/Yesod/Helpers/Auth/Email.hs @@ -1,5 +1,6 @@ {-# LANGUAGE QuasiQuotes, TypeFamilies #-} {-# LANGUAGE CPP #-} +{-# LANGUAGE OverloadedStrings #-} module Yesod.Helpers.Auth.Email ( -- * Plugin authEmail @@ -19,7 +20,9 @@ import Control.Monad (when) import Control.Applicative ((<$>), (<*>)) import Data.Digest.Pure.MD5 import qualified Data.Text.Lazy as T +import qualified Data.Text as TS import Data.Text.Lazy.Encoding (encodeUtf8) +import Data.Text (Text) import Yesod.Form import Yesod.Handler @@ -35,13 +38,13 @@ loginR = PluginR "email" ["login"] registerR = PluginR "email" ["register"] setpassR = PluginR "email" ["set-password"] -verify :: String -> String -> AuthRoute -- FIXME +verify :: Text -> Text -> AuthRoute -- FIXME verify eid verkey = PluginR "email" ["verify", eid, verkey] -type Email = String -type VerKey = String -type VerUrl = String -type SaltedPass = String +type Email = Text +type VerKey = Text +type VerUrl = Text +type SaltedPass = Text type VerStatus = Bool -- | Data stored in a database for each e-mail address. @@ -55,8 +58,8 @@ data EmailCreds m = EmailCreds class YesodAuth m => YesodAuthEmail m where type AuthEmailId m - showAuthEmailId :: m -> AuthEmailId m -> String - readAuthEmailId :: m -> String -> Maybe (AuthEmailId m) + showAuthEmailId :: m -> AuthEmailId m -> Text + readAuthEmailId :: m -> Text -> Maybe (AuthEmailId m) addUnverified :: Email -> VerKey -> GHandler Auth m (AuthEmailId m) sendVerifyEmail :: Email -> VerKey -> VerUrl -> GHandler Auth m () @@ -69,10 +72,10 @@ class YesodAuth m => YesodAuthEmail m where getEmail :: AuthEmailId m -> GHandler Auth m (Maybe Email) -- | Generate a random alphanumeric string. - randomKey :: m -> IO String + randomKey :: m -> IO Text randomKey _ = do stdgen <- newStdGen - return $ fst $ randomString 10 stdgen + return $ TS.pack $ fst $ randomString 10 stdgen authEmail :: YesodAuthEmail m => AuthPlugin m authEmail = @@ -162,7 +165,7 @@ postRegisterR = do |] getVerifyR :: YesodAuthEmail m - => AuthEmailId m -> String -> GHandler Auth m RepHtml + => AuthEmailId m -> Text -> GHandler Auth m RepHtml getVerifyR lid key = do realKey <- getVerifyKey lid memail <- getEmail lid @@ -274,11 +277,11 @@ saltLength :: Int saltLength = 5 -- | Salt a password with a randomly generated salt. -saltPass :: String -> IO String +saltPass :: Text -> IO Text saltPass pass = do stdgen <- newStdGen let salt = take saltLength $ randomRs ('A', 'Z') stdgen - return $ saltPass' salt pass + return $ TS.pack $ saltPass' salt $ TS.unpack pass saltPass' :: String -> String -> String saltPass' salt pass = @@ -286,9 +289,12 @@ saltPass' salt pass = where fromString = encodeUtf8 . T.pack -isValidPass :: String -- ^ cleartext password +isValidPass :: Text -- ^ cleartext password -> SaltedPass -- ^ salted password -> Bool -isValidPass clear salted = +isValidPass clear' salted' = let salt = take saltLength salted in salted == saltPass' salt clear + where + clear = TS.unpack clear' + salted = TS.unpack salted' diff --git a/Yesod/Helpers/Auth/Facebook.hs b/Yesod/Helpers/Auth/Facebook.hs index 5c049e49..5517928e 100644 --- a/Yesod/Helpers/Auth/Facebook.hs +++ b/Yesod/Helpers/Auth/Facebook.hs @@ -1,5 +1,6 @@ {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE CPP #-} +{-# LANGUAGE OverloadedStrings #-} module Yesod.Helpers.Auth.Facebook ( authFacebook , facebookUrl @@ -7,7 +8,8 @@ module Yesod.Helpers.Auth.Facebook import Yesod.Helpers.Auth import qualified Web.Authenticate.Facebook as Facebook -import Data.Object (fromMapping, lookupScalar) +import Data.Aeson +import Data.Aeson.Types (parseMaybe) import Data.Maybe (fromMaybe) import Yesod.Form @@ -17,14 +19,17 @@ import Text.Hamlet (hamlet) import Control.Monad.IO.Class (liftIO) import qualified Data.ByteString.Char8 as S8 import Control.Monad.Trans.Class (lift) +import Data.Text (Text) +import Control.Monad (mzero) +import Data.Monoid (mappend) facebookUrl :: AuthRoute facebookUrl = PluginR "facebook" ["forward"] authFacebook :: YesodAuth m - => String -- ^ Application ID - -> String -- ^ Application secret - -> [String] -- ^ Requested permissions + => Text -- ^ Application ID + -> Text -- ^ Application secret + -> [Text] -- ^ Requested permissions -> AuthPlugin m authFacebook cid secret perms = AuthPlugin "facebook" dispatch login @@ -34,7 +39,7 @@ authFacebook cid secret perms = tm <- getRouteToMaster render <- getUrlRender let fb = Facebook.Facebook cid secret $ render $ tm url - redirectString RedirectTemporary $ S8.pack $ Facebook.getForwardUrl fb perms + redirectString RedirectTemporary $ Facebook.getForwardUrl fb perms dispatch "GET" [] = do render <- getUrlRender tm <- getRouteToMaster @@ -43,18 +48,8 @@ authFacebook cid secret perms = at <- liftIO $ Facebook.getAccessToken fb code let Facebook.AccessToken at' = at so <- liftIO $ Facebook.getGraphData at "me" - let c = fromMaybe (error "Invalid response from Facebook") $ do - m <- fromMapping so - id' <- lookupScalar "id" m - let name = lookupScalar "name" m - let email = lookupScalar "email" m - let id'' = "http://graph.facebook.com/" ++ id' - return - $ Creds "facebook" id'' - $ maybe id (\x -> (:) ("verifiedEmail", x)) email - $ maybe id (\x -> (:) ("displayName ", x)) name - [ ("accessToken", at') - ] + let c = fromMaybe (error "Invalid response from Facebook") + $ parseMaybe (parseCreds at') $ either error id so setCreds True c dispatch _ _ = notFound login tm = do @@ -71,3 +66,16 @@ authFacebook cid secret perms =