Cleanup, use SinglePiece

This commit is contained in:
Michael Snoyman 2011-04-15 12:32:07 +03:00
parent eb94784201
commit 52bb6fdc50
7 changed files with 45 additions and 63 deletions

View File

@ -22,20 +22,13 @@ module Yesod.Helpers.Auth
, requireAuth , requireAuth
) where ) where
import Yesod.Handler
import Yesod.Core import Yesod.Core
import Yesod.Widget
import Yesod.Content
import Yesod.Dispatch
import Yesod.Persist import Yesod.Persist
import Yesod.Request
import Yesod.Json import Yesod.Json
import Text.Blaze import Text.Blaze
import Language.Haskell.TH.Syntax hiding (lift) import Language.Haskell.TH.Syntax hiding (lift)
import qualified Data.ByteString.Char8 as S8
import qualified Network.Wai as W import qualified Network.Wai as W
import Text.Hamlet (hamlet) import Text.Hamlet (hamlet)
import Data.Text.Lazy (pack)
import qualified Data.Map as Map import qualified Data.Map as Map
import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Class (lift)
import Data.Aeson import Data.Aeson
@ -66,7 +59,7 @@ data Creds m = Creds
, credsExtra :: [(Text, Text)] , credsExtra :: [(Text, Text)]
} }
class Yesod m => YesodAuth m where class (Yesod m, SinglePiece (AuthId m)) => YesodAuth m where
type AuthId m type AuthId m
-- | Default destination on successful login, if no other -- | Default destination on successful login, if no other
@ -79,65 +72,62 @@ class Yesod m => YesodAuth m where
getAuthId :: Creds m -> GHandler s m (Maybe (AuthId m)) getAuthId :: Creds m -> GHandler s m (Maybe (AuthId m))
showAuthId :: m -> AuthId m -> Text
readAuthId :: m -> Text -> Maybe (AuthId m)
authPlugins :: [AuthPlugin m] authPlugins :: [AuthPlugin m]
-- | What to show on the login page. -- | What to show on the login page.
loginHandler :: GHandler Auth m RepHtml loginHandler :: GHandler Auth m RepHtml
loginHandler = defaultLayout $ do loginHandler = defaultLayout $ do
setTitle $ string "Login" setTitle "Login"
tm <- lift getRouteToMaster tm <- lift getRouteToMaster
mapM_ (flip apLogin tm) authPlugins mapM_ (flip apLogin tm) authPlugins
----- Message strings. In theory in the future make this localizable ----- Message strings. In theory in the future make this localizable
----- See gist: https://gist.github.com/778712 ----- See gist: https://gist.github.com/778712
messageNoOpenID :: m -> Html messageNoOpenID :: m -> Html
messageNoOpenID _ = string "No OpenID identifier found" messageNoOpenID _ = "No OpenID identifier found"
messageLoginOpenID :: m -> Html messageLoginOpenID :: m -> Html
messageLoginOpenID _ = string "Login via OpenID" messageLoginOpenID _ = "Login via OpenID"
messageEmail :: m -> Html messageEmail :: m -> Html
messageEmail _ = string "Email" messageEmail _ = "Email"
messagePassword :: m -> Html messagePassword :: m -> Html
messagePassword _ = string "Password" messagePassword _ = "Password"
messageRegister :: m -> Html messageRegister :: m -> Html
messageRegister _ = string "Register" messageRegister _ = "Register"
messageRegisterLong :: m -> Html messageRegisterLong :: m -> Html
messageRegisterLong _ = string "Register a new account" messageRegisterLong _ = "Register a new account"
messageEnterEmail :: m -> Html messageEnterEmail :: m -> Html
messageEnterEmail _ = string "Enter your e-mail address below, and a confirmation e-mail will be sent to you." messageEnterEmail _ = "Enter your e-mail address below, and a confirmation e-mail will be sent to you."
messageConfirmationEmailSentTitle :: m -> Html messageConfirmationEmailSentTitle :: m -> Html
messageConfirmationEmailSentTitle _ = string "Confirmation e-mail sent" messageConfirmationEmailSentTitle _ = "Confirmation e-mail sent"
messageConfirmationEmailSent :: m -> Text -> Html messageConfirmationEmailSent :: m -> Text -> Html
messageConfirmationEmailSent _ email = toHtml $ mconcat messageConfirmationEmailSent _ email = toHtml $ mconcat
["A confirmation e-mail has been sent to ", email, "."] ["A confirmation e-mail has been sent to ", email, "."]
messageAddressVerified :: m -> Html messageAddressVerified :: m -> Html
messageAddressVerified _ = string "Address verified, please set a new password" messageAddressVerified _ = "Address verified, please set a new password"
messageInvalidKeyTitle :: m -> Html messageInvalidKeyTitle :: m -> Html
messageInvalidKeyTitle _ = string "Invalid verification key" messageInvalidKeyTitle _ = "Invalid verification key"
messageInvalidKey :: m -> Html messageInvalidKey :: m -> Html
messageInvalidKey _ = string "I'm sorry, but that was an invalid verification key." messageInvalidKey _ = "I'm sorry, but that was an invalid verification key."
messageInvalidEmailPass :: m -> Html messageInvalidEmailPass :: m -> Html
messageInvalidEmailPass _ = string "Invalid email/password combination" messageInvalidEmailPass _ = "Invalid email/password combination"
messageBadSetPass :: m -> Html messageBadSetPass :: m -> Html
messageBadSetPass _ = string "You must be logged in to set a password" messageBadSetPass _ = "You must be logged in to set a password"
messageSetPassTitle :: m -> Html messageSetPassTitle :: m -> Html
messageSetPassTitle _ = string "Set password" messageSetPassTitle _ = "Set password"
messageSetPass :: m -> Html messageSetPass :: m -> Html
messageSetPass _ = string "Set a new password" messageSetPass _ = "Set a new password"
messageNewPass :: m -> Html messageNewPass :: m -> Html
messageNewPass _ = string "New password" messageNewPass _ = "New password"
messageConfirmPass :: m -> Html messageConfirmPass :: m -> Html
messageConfirmPass _ = string "Confirm" messageConfirmPass _ = "Confirm"
messagePassMismatch :: m -> Html messagePassMismatch :: m -> Html
messagePassMismatch _ = string "Passwords did not match, please try again" messagePassMismatch _ = "Passwords did not match, please try again"
messagePassUpdated :: m -> Html messagePassUpdated :: m -> Html
messagePassUpdated _ = string "Password updated" messagePassUpdated _ = "Password updated"
messageFacebook :: m -> Html messageFacebook :: m -> Html
messageFacebook _ = string "Login with Facebook" messageFacebook _ = "Login with Facebook"
type Texts = [Text] type Texts = [Text]
@ -180,14 +170,14 @@ setCreds doRedirects creds = do
|] |]
sendResponse rh sendResponse rh
Just ar -> do Just ar -> do
setMessage $ string "Invalid login" setMessage "Invalid login"
redirect RedirectTemporary ar redirect RedirectTemporary ar
else return () else return ()
Just aid -> do Just aid -> do
setSession credsKey $ showAuthId y aid setSession credsKey $ toSinglePiece aid
if doRedirects if doRedirects
then do then do
setMessage $ string "You are now logged in" setMessage "You are now logged in"
redirectUltDest RedirectTemporary $ loginDest y redirectUltDest RedirectTemporary $ loginDest y
else return () else return ()
@ -195,8 +185,8 @@ getCheckR :: YesodAuth m => GHandler Auth m RepHtmlJson
getCheckR = do getCheckR = do
creds <- maybeAuthId creds <- maybeAuthId
defaultLayoutJson (do defaultLayoutJson (do
setTitle $ string "Authentication Status" setTitle "Authentication Status"
addHtml $ html creds) (json creds) addHtml $ html creds) (json' creds)
where where
html creds = html creds =
#if GHC7 #if GHC7
@ -210,7 +200,7 @@ $maybe _ <- creds
$nothing $nothing
<p>Not logged in. <p>Not logged in.
|] |]
json creds = json' creds =
Object $ Map.fromList Object $ Map.fromList
[ (T.pack "logged_in", Bool $ maybe False (const True) creds) [ (T.pack "logged_in", Bool $ maybe False (const True) creds)
] ]
@ -239,10 +229,9 @@ handlePluginR plugin pieces = do
maybeAuthId :: YesodAuth m => GHandler s m (Maybe (AuthId m)) maybeAuthId :: YesodAuth m => GHandler s m (Maybe (AuthId m))
maybeAuthId = do maybeAuthId = do
ms <- lookupSession credsKey ms <- lookupSession credsKey
y <- getYesod
case ms of case ms of
Nothing -> return Nothing Nothing -> return Nothing
Just s -> return $ readAuthId y s Just s -> return $ fromSinglePiece s
maybeAuth :: ( YesodAuth m maybeAuth :: ( YesodAuth m
, Key val ~ AuthId m , Key val ~ AuthId m

View File

@ -1,6 +1,7 @@
{-# LANGUAGE QuasiQuotes, TypeFamilies #-} {-# LANGUAGE QuasiQuotes, TypeFamilies #-}
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
module Yesod.Helpers.Auth.Email module Yesod.Helpers.Auth.Email
( -- * Plugin ( -- * Plugin
authEmail authEmail
@ -55,12 +56,9 @@ data EmailCreds m = EmailCreds
, emailCredsVerkey :: Maybe VerKey , emailCredsVerkey :: Maybe VerKey
} }
class YesodAuth m => YesodAuthEmail m where class (YesodAuth m, SinglePiece (AuthEmailId m)) => YesodAuthEmail m where
type AuthEmailId m type AuthEmailId m
showAuthEmailId :: m -> AuthEmailId m -> Text
readAuthEmailId :: m -> Text -> Maybe (AuthEmailId m)
addUnverified :: Email -> VerKey -> GHandler Auth m (AuthEmailId m) addUnverified :: Email -> VerKey -> GHandler Auth m (AuthEmailId m)
sendVerifyEmail :: Email -> VerKey -> VerUrl -> GHandler Auth m () sendVerifyEmail :: Email -> VerKey -> VerUrl -> GHandler Auth m ()
getVerifyKey :: AuthEmailId m -> GHandler Auth m (Maybe VerKey) getVerifyKey :: AuthEmailId m -> GHandler Auth m (Maybe VerKey)
@ -104,9 +102,8 @@ authEmail =
where where
dispatch "GET" ["register"] = getRegisterR >>= sendResponse dispatch "GET" ["register"] = getRegisterR >>= sendResponse
dispatch "POST" ["register"] = postRegisterR >>= sendResponse dispatch "POST" ["register"] = postRegisterR >>= sendResponse
dispatch "GET" ["verify", eid, verkey] = do dispatch "GET" ["verify", eid, verkey] =
y <- getYesod case fromSinglePiece eid of
case readAuthEmailId y eid of
Nothing -> notFound Nothing -> notFound
Just eid' -> getVerifyR eid' verkey >>= sendResponse Just eid' -> getVerifyR eid' verkey >>= sendResponse
dispatch "POST" ["login"] = postLoginR >>= sendResponse dispatch "POST" ["login"] = postLoginR >>= sendResponse
@ -151,7 +148,7 @@ postRegisterR = do
return (lid, key) return (lid, key)
render <- getUrlRender render <- getUrlRender
tm <- getRouteToMaster tm <- getRouteToMaster
let verUrl = render $ tm $ verify (showAuthEmailId y lid) verKey let verUrl = render $ tm $ verify (toSinglePiece lid) verKey
sendVerifyEmail email verKey verUrl sendVerifyEmail email verKey verUrl
defaultLayout $ do defaultLayout $ do
setTitle $ messageConfirmationEmailSentTitle y setTitle $ messageConfirmationEmailSentTitle y

View File

@ -17,11 +17,11 @@ import Yesod.Handler
import Yesod.Widget import Yesod.Widget
import Text.Hamlet (hamlet) import Text.Hamlet (hamlet)
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import qualified Data.ByteString.Char8 as S8
import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Class (lift)
import Data.Text (Text) import Data.Text (Text)
import Control.Monad (mzero) import Control.Monad (mzero)
import Data.Monoid (mappend) import Data.Monoid (mappend)
import qualified Data.Aeson.Types
facebookUrl :: AuthRoute facebookUrl :: AuthRoute
facebookUrl = PluginR "facebook" ["forward"] facebookUrl = PluginR "facebook" ["forward"]
@ -39,7 +39,7 @@ authFacebook cid secret perms =
tm <- getRouteToMaster tm <- getRouteToMaster
render <- getUrlRender render <- getUrlRender
let fb = Facebook.Facebook cid secret $ render $ tm url let fb = Facebook.Facebook cid secret $ render $ tm url
redirectString RedirectTemporary $ Facebook.getForwardUrl fb perms redirectText RedirectTemporary $ Facebook.getForwardUrl fb perms
dispatch "GET" [] = do dispatch "GET" [] = do
render <- getUrlRender render <- getUrlRender
tm <- getRouteToMaster tm <- getRouteToMaster
@ -67,6 +67,7 @@ authFacebook cid secret perms =
<a href="#{furl}">#{messageFacebook y} <a href="#{furl}">#{messageFacebook y}
|] |]
parseCreds :: Text -> Value -> Data.Aeson.Types.Parser (Creds m)
parseCreds at' (Object m) = do parseCreds at' (Object m) = do
id' <- m .: "id" id' <- m .: "id"
let id'' = "http://graph.facebook.com/" `mappend` id' let id'' = "http://graph.facebook.com/" `mappend` id'

View File

@ -73,7 +73,6 @@ import Text.Hamlet (hamlet)
import Control.Applicative ((<$>), (<*>)) import Control.Applicative ((<$>), (<*>))
import Data.ByteString.Lazy.Char8 (pack) import Data.ByteString.Lazy.Char8 (pack)
import Data.Digest.Pure.SHA (sha1, showDigest) import Data.Digest.Pure.SHA (sha1, showDigest)
import Database.Persist.TH (share2, mkMigrate, persist, mkPersist)
import Data.Text (Text, unpack) import Data.Text (Text, unpack)
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)

View File

@ -14,15 +14,14 @@ import Text.Hamlet (hamlet)
import Web.Authenticate.OAuth import Web.Authenticate.OAuth
import Data.Maybe import Data.Maybe
import Data.String import Data.String
import Network.HTTP.Enumerator
import Data.ByteString.Char8 (pack) import Data.ByteString.Char8 (pack)
import Control.Arrow ((***)) import Control.Arrow ((***))
import Control.Monad
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Class (lift)
import Data.Text (Text, unpack) import Data.Text (Text, unpack)
import Data.Text.Encoding (encodeUtf8, decodeUtf8With) import Data.Text.Encoding (encodeUtf8, decodeUtf8With)
import Data.Text.Encoding.Error (lenientDecode) import Data.Text.Encoding.Error (lenientDecode)
import Data.ByteString (ByteString)
oauthUrl :: Text -> AuthRoute oauthUrl :: Text -> AuthRoute
oauthUrl name = PluginR name ["forward"] oauthUrl name = PluginR name ["forward"]
@ -50,11 +49,8 @@ authOAuth name ident reqUrl accUrl authUrl key sec = AuthPlugin name dispatch lo
tm <- getRouteToMaster tm <- getRouteToMaster
let oauth' = oauth { oauthCallback = Just $ encodeUtf8 $ render $ tm url } let oauth' = oauth { oauthCallback = Just $ encodeUtf8 $ render $ tm url }
tok <- liftIO $ getTemporaryCredential oauth' tok <- liftIO $ getTemporaryCredential oauth'
redirectString RedirectTemporary (fromString $ authorizeUrl oauth' tok) redirectText RedirectTemporary (fromString $ authorizeUrl oauth' tok)
dispatch "GET" [] = do dispatch "GET" [] = do
render <- getUrlRender
tm <- getRouteToMaster
let callback = render $ tm url
verifier <- runFormGet' $ stringInput "oauth_verifier" verifier <- runFormGet' $ stringInput "oauth_verifier"
oaTok <- runFormGet' $ stringInput "oauth_token" oaTok <- runFormGet' $ stringInput "oauth_token"
let reqTok = Credential [ ("oauth_verifier", encodeUtf8 verifier), ("oauth_token", encodeUtf8 oaTok) let reqTok = Credential [ ("oauth_verifier", encodeUtf8 verifier), ("oauth_token", encodeUtf8 oaTok)
@ -89,4 +85,5 @@ authTwitter = authOAuth "twitter"
twitterUrl :: AuthRoute twitterUrl :: AuthRoute
twitterUrl = oauthUrl "twitter" twitterUrl = oauthUrl "twitter"
bsToText :: ByteString -> Text
bsToText = decodeUtf8With lenientDecode bsToText = decodeUtf8With lenientDecode

View File

@ -16,9 +16,8 @@ import Yesod.Widget
import Yesod.Request import Yesod.Request
import Text.Hamlet (hamlet) import Text.Hamlet (hamlet)
import Text.Cassius (cassius) import Text.Cassius (cassius)
import Text.Blaze (string) import Text.Blaze (toHtml)
import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Class (lift)
import qualified Data.ByteString.Char8 as S8
import Data.Text (Text) import Data.Text (Text)
forwardUrl :: AuthRoute forwardUrl :: AuthRoute
@ -64,10 +63,10 @@ authOpenId =
res <- runAttemptT $ OpenId.getForwardUrl oid complete' Nothing [] res <- runAttemptT $ OpenId.getForwardUrl oid complete' Nothing []
attempt attempt
(\err -> do (\err -> do
setMessage $ string $ show err setMessage $ toHtml $ show err
redirect RedirectTemporary $ toMaster LoginR redirect RedirectTemporary $ toMaster LoginR
) )
(redirectString RedirectTemporary) (redirectText RedirectTemporary)
res res
_ -> do _ -> do
toMaster <- getRouteToMaster toMaster <- getRouteToMaster
@ -88,7 +87,7 @@ completeHelper gets' = do
res <- runAttemptT $ OpenId.authenticate gets' res <- runAttemptT $ OpenId.authenticate gets'
toMaster <- getRouteToMaster toMaster <- getRouteToMaster
let onFailure err = do let onFailure err = do
setMessage $ string $ show err setMessage $ toHtml $ show err
redirect RedirectTemporary $ toMaster LoginR redirect RedirectTemporary $ toMaster LoginR
let onSuccess (OpenId.Identifier ident, _) = let onSuccess (OpenId.Identifier ident, _) =
setCreds True $ Creds "openid" ident [] setCreds True $ Creds "openid" ident []

View File

@ -14,7 +14,7 @@ import Yesod.Widget
import Yesod.Request import Yesod.Request
import Text.Hamlet (hamlet) import Text.Hamlet (hamlet)
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import Data.Text (Text, pack, unpack) import Data.Text (pack, unpack)
import Control.Arrow ((***)) import Control.Arrow ((***))
authRpxnow :: YesodAuth m authRpxnow :: YesodAuth m