It all compiles
This commit is contained in:
parent
aed10fc84a
commit
8e265f6ebc
@ -1,5 +1,8 @@
|
|||||||
{-# LANGUAGE DeriveDataTypeable, OverloadedStrings, QuasiQuotes #-}
|
{-# LANGUAGE DeriveDataTypeable, OverloadedStrings, QuasiQuotes #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE RankNTypes #-}
|
||||||
|
{-# LANGUAGE GADTs #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
module Yesod.Auth.OAuth
|
module Yesod.Auth.OAuth
|
||||||
( authOAuth
|
( authOAuth
|
||||||
, oauthUrl
|
, oauthUrl
|
||||||
@ -14,6 +17,7 @@ import Control.Applicative as A ((<$>), (<*>))
|
|||||||
import Control.Arrow ((***))
|
import Control.Arrow ((***))
|
||||||
import Control.Exception.Lifted
|
import Control.Exception.Lifted
|
||||||
import Control.Monad.IO.Class
|
import Control.Monad.IO.Class
|
||||||
|
import Control.Monad.IO.Unlift (MonadUnliftIO)
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
@ -35,26 +39,37 @@ instance Exception YesodOAuthException
|
|||||||
oauthUrl :: Text -> AuthRoute
|
oauthUrl :: Text -> AuthRoute
|
||||||
oauthUrl name = PluginR name ["forward"]
|
oauthUrl name = PluginR name ["forward"]
|
||||||
|
|
||||||
authOAuth :: YesodAuth m
|
authOAuth :: forall master. YesodAuth master
|
||||||
=> OAuth -- ^ 'OAuth' data-type for signing.
|
=> OAuth -- ^ 'OAuth' data-type for signing.
|
||||||
-> (Credential -> IO (Creds m)) -- ^ How to extract ident.
|
-> (Credential -> IO (Creds master)) -- ^ How to extract ident.
|
||||||
-> AuthPlugin m
|
-> AuthPlugin master
|
||||||
authOAuth oauth mkCreds = AuthPlugin name dispatch login
|
authOAuth oauth mkCreds = AuthPlugin name dispatch login
|
||||||
where
|
where
|
||||||
name = T.pack $ oauthServerName oauth
|
name = T.pack $ oauthServerName oauth
|
||||||
url = PluginR name []
|
url = PluginR name []
|
||||||
lookupTokenSecret = bsToText . fromMaybe "" . lookup "oauth_token_secret" . unCredential
|
lookupTokenSecret = bsToText . fromMaybe "" . lookup "oauth_token_secret" . unCredential
|
||||||
|
|
||||||
|
oauthSessionName :: Text
|
||||||
oauthSessionName = "__oauth_token_secret"
|
oauthSessionName = "__oauth_token_secret"
|
||||||
|
|
||||||
|
dispatch
|
||||||
|
:: ( MonadSubHandler m
|
||||||
|
, master ~ HandlerSite m
|
||||||
|
, Auth ~ SubHandlerSite m
|
||||||
|
, MonadUnliftIO m
|
||||||
|
)
|
||||||
|
=> Text
|
||||||
|
-> [Text]
|
||||||
|
-> m TypedContent
|
||||||
dispatch "GET" ["forward"] = do
|
dispatch "GET" ["forward"] = do
|
||||||
render <- lift getUrlRender
|
render <- getUrlRender
|
||||||
tm <- getRouteToParent
|
tm <- getRouteToParent
|
||||||
let oauth' = oauth { oauthCallback = Just $ encodeUtf8 $ render $ tm url }
|
let oauth' = oauth { oauthCallback = Just $ encodeUtf8 $ render $ tm url }
|
||||||
master <- lift getYesod
|
manager <- authHttpManager
|
||||||
tok <- lift $ getTemporaryCredential oauth' (authHttpManager master)
|
tok <- getTemporaryCredential oauth' manager
|
||||||
setSession oauthSessionName $ lookupTokenSecret tok
|
setSession oauthSessionName $ lookupTokenSecret tok
|
||||||
redirect $ authorizeUrl oauth' tok
|
redirect $ authorizeUrl oauth' tok
|
||||||
dispatch "GET" [] = lift $ do
|
dispatch "GET" [] = do
|
||||||
Just tokSec <- lookupSession oauthSessionName
|
Just tokSec <- lookupSession oauthSessionName
|
||||||
deleteSession oauthSessionName
|
deleteSession oauthSessionName
|
||||||
reqTok <-
|
reqTok <-
|
||||||
@ -72,8 +87,8 @@ authOAuth oauth mkCreds = AuthPlugin name dispatch login
|
|||||||
, ("oauth_token", encodeUtf8 oaTok)
|
, ("oauth_token", encodeUtf8 oaTok)
|
||||||
, ("oauth_token_secret", encodeUtf8 tokSec)
|
, ("oauth_token_secret", encodeUtf8 tokSec)
|
||||||
]
|
]
|
||||||
master <- getYesod
|
manager <- authHttpManager
|
||||||
accTok <- getAccessToken oauth reqTok (authHttpManager master)
|
accTok <- getAccessToken oauth reqTok manager
|
||||||
creds <- liftIO $ mkCreds accTok
|
creds <- liftIO $ mkCreds accTok
|
||||||
setCredsRedirect creds
|
setCredsRedirect creds
|
||||||
dispatch _ _ = notFound
|
dispatch _ _ = notFound
|
||||||
|
|||||||
@ -29,6 +29,7 @@ library
|
|||||||
, yesod-form >= 1.4 && < 1.5
|
, yesod-form >= 1.4 && < 1.5
|
||||||
, transformers >= 0.2.2 && < 0.6
|
, transformers >= 0.2.2 && < 0.6
|
||||||
, lifted-base >= 0.2 && < 0.3
|
, lifted-base >= 0.2 && < 0.3
|
||||||
|
, unliftio-core
|
||||||
exposed-modules: Yesod.Auth.OAuth
|
exposed-modules: Yesod.Auth.OAuth
|
||||||
ghc-options: -Wall
|
ghc-options: -Wall
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,7 @@ module Yesod.Auth
|
|||||||
-- * Exception
|
-- * Exception
|
||||||
, AuthException (..)
|
, AuthException (..)
|
||||||
-- * Helper
|
-- * Helper
|
||||||
|
, MonadAuthHandler
|
||||||
, AuthHandler
|
, AuthHandler
|
||||||
-- * Internal
|
-- * Internal
|
||||||
, credsKey
|
, credsKey
|
||||||
@ -49,8 +50,7 @@ module Yesod.Auth
|
|||||||
|
|
||||||
import Control.Monad (when)
|
import Control.Monad (when)
|
||||||
import Control.Monad.Trans.Maybe
|
import Control.Monad.Trans.Maybe
|
||||||
import Control.Monad.Trans.Reader (ReaderT)
|
import Control.Monad.IO.Unlift (withRunInIO, MonadUnliftIO)
|
||||||
import Control.Monad.IO.Unlift (withRunInIO)
|
|
||||||
|
|
||||||
import Yesod.Auth.Routes
|
import Yesod.Auth.Routes
|
||||||
import Data.Aeson hiding (json)
|
import Data.Aeson hiding (json)
|
||||||
@ -78,7 +78,8 @@ import Control.Monad (void)
|
|||||||
|
|
||||||
type AuthRoute = Route Auth
|
type AuthRoute = Route Auth
|
||||||
|
|
||||||
type AuthHandler master a = forall m. (MonadSubHandler m, YesodAuth master, master ~ HandlerSite m, Auth ~ SubHandlerSite m) => m a
|
type MonadAuthHandler master m = (MonadSubHandler m, YesodAuth master, master ~ HandlerSite m, Auth ~ SubHandlerSite m, MonadUnliftIO m)
|
||||||
|
type AuthHandler master a = forall m. MonadAuthHandler master m => m a
|
||||||
|
|
||||||
type Method = Text
|
type Method = Text
|
||||||
type Piece = Text
|
type Piece = Text
|
||||||
@ -192,8 +193,8 @@ class (Yesod master, PathPiece (AuthId master), RenderMessage master FormMessage
|
|||||||
-- type. This allows backends to reuse persistent connections. If none of
|
-- type. This allows backends to reuse persistent connections. If none of
|
||||||
-- the backends you're using use HTTP connections, you can safely return
|
-- the backends you're using use HTTP connections, you can safely return
|
||||||
-- @error \"authHttpManager\"@ here.
|
-- @error \"authHttpManager\"@ here.
|
||||||
authHttpManager :: master -> IO Manager
|
authHttpManager :: AuthHandler master Manager
|
||||||
authHttpManager _ = getGlobalManager
|
authHttpManager = liftIO getGlobalManager
|
||||||
|
|
||||||
-- | Called on a successful login. By default, calls
|
-- | Called on a successful login. By default, calls
|
||||||
-- @addMessageI "success" NowLoggedIn@.
|
-- @addMessageI "success" NowLoggedIn@.
|
||||||
@ -232,13 +233,14 @@ class (Yesod master, PathPiece (AuthId master), RenderMessage master FormMessage
|
|||||||
--
|
--
|
||||||
-- The HTTP 'Request' is given in case it is useful to change behavior based on inspecting the request.
|
-- The HTTP 'Request' is given in case it is useful to change behavior based on inspecting the request.
|
||||||
-- This is an experimental API that is not broadly used throughout the yesod-auth code base
|
-- This is an experimental API that is not broadly used throughout the yesod-auth code base
|
||||||
runHttpRequest :: (MonadSubHandler m, HandlerSite m ~ master, SubHandlerSite m ~ Auth)
|
runHttpRequest
|
||||||
=> Request
|
:: MonadAuthHandler master m
|
||||||
-> (Response BodyReader -> ReaderT (SubsiteData Auth master) (HandlerFor master) a)
|
=> Request
|
||||||
-> m a
|
-> (Response BodyReader -> m a)
|
||||||
|
-> m a
|
||||||
runHttpRequest req inner = do
|
runHttpRequest req inner = do
|
||||||
man <- getYesod >>= liftIO . authHttpManager
|
man <- authHttpManager
|
||||||
lift $ withRunInIO $ \run -> withResponse req man $ run . inner
|
withRunInIO $ \run -> withResponse req man $ run . inner
|
||||||
|
|
||||||
{-# MINIMAL loginDest, logoutDest, (authenticate | getAuthId), authPlugins, authHttpManager #-}
|
{-# MINIMAL loginDest, logoutDest, (authenticate | getAuthId), authPlugins, authHttpManager #-}
|
||||||
|
|
||||||
@ -268,7 +270,8 @@ defaultMaybeAuthId = runMaybeT $ do
|
|||||||
|
|
||||||
cachedAuth
|
cachedAuth
|
||||||
:: (YesodAuthPersist master, Typeable (AuthEntity master))
|
:: (YesodAuthPersist master, Typeable (AuthEntity master))
|
||||||
=> AuthId master -> AuthHandler master (Maybe (AuthEntity master))
|
=> AuthId master
|
||||||
|
-> AuthHandler master (Maybe (AuthEntity master))
|
||||||
cachedAuth
|
cachedAuth
|
||||||
= fmap unCachedMaybeAuth
|
= fmap unCachedMaybeAuth
|
||||||
. cached
|
. cached
|
||||||
@ -285,25 +288,25 @@ cachedAuth
|
|||||||
defaultLoginHandler :: AuthHandler master Html
|
defaultLoginHandler :: AuthHandler master Html
|
||||||
defaultLoginHandler = do
|
defaultLoginHandler = do
|
||||||
tp <- getRouteToParent
|
tp <- getRouteToParent
|
||||||
liftHandler $ authLayout $ do
|
authLayout $ do
|
||||||
setTitleI Msg.LoginTitle
|
setTitleI Msg.LoginTitle
|
||||||
master <- getYesod
|
master <- getYesod
|
||||||
mapM_ (flip apLogin tp) (authPlugins master)
|
mapM_ (flip apLogin tp) (authPlugins master)
|
||||||
|
|
||||||
|
|
||||||
loginErrorMessageI :: (YesodAuth (HandlerSite m), MonadSubHandler m)
|
loginErrorMessageI
|
||||||
=> Route (SubHandlerSite m)
|
:: Route Auth
|
||||||
-> AuthMessage
|
-> AuthMessage
|
||||||
-> m TypedContent
|
-> AuthHandler master TypedContent
|
||||||
loginErrorMessageI dest msg = do
|
loginErrorMessageI dest msg = do
|
||||||
toParent <- getRouteToParent
|
toParent <- getRouteToParent
|
||||||
liftHandler $ loginErrorMessageMasterI (toParent dest) msg
|
loginErrorMessageMasterI (toParent dest) msg
|
||||||
|
|
||||||
|
|
||||||
loginErrorMessageMasterI :: (YesodAuth master, RenderMessage master AuthMessage)
|
loginErrorMessageMasterI
|
||||||
=> Route master
|
:: Route master
|
||||||
-> AuthMessage
|
-> AuthMessage
|
||||||
-> AuthHandler master TypedContent
|
-> AuthHandler master TypedContent
|
||||||
loginErrorMessageMasterI dest msg = do
|
loginErrorMessageMasterI dest msg = do
|
||||||
mr <- getMessageRender
|
mr <- getMessageRender
|
||||||
loginErrorMessage dest (mr msg)
|
loginErrorMessage dest (mr msg)
|
||||||
@ -316,19 +319,22 @@ loginErrorMessage :: YesodAuth master
|
|||||||
-> AuthHandler master TypedContent
|
-> AuthHandler master TypedContent
|
||||||
loginErrorMessage dest msg = messageJson401 msg (onErrorHtml dest msg)
|
loginErrorMessage dest msg = messageJson401 msg (onErrorHtml dest msg)
|
||||||
|
|
||||||
messageJson401 :: (MonadSubHandler m, HandlerSite m ~ master, SubHandlerSite m ~ Auth)
|
messageJson401
|
||||||
=> Text
|
:: MonadAuthHandler master m
|
||||||
-> m Html
|
=> Text
|
||||||
-> m TypedContent
|
-> m Html
|
||||||
|
-> m TypedContent
|
||||||
messageJson401 = messageJsonStatus unauthorized401
|
messageJson401 = messageJsonStatus unauthorized401
|
||||||
|
|
||||||
messageJson500 :: Text -> HandlerFor master Html -> HandlerFor master TypedContent
|
messageJson500 :: MonadAuthHandler master m => Text -> m Html -> m TypedContent
|
||||||
messageJson500 = messageJsonStatus internalServerError500
|
messageJson500 = messageJsonStatus internalServerError500
|
||||||
|
|
||||||
messageJsonStatus :: Status
|
messageJsonStatus
|
||||||
-> Text
|
:: MonadAuthHandler master m
|
||||||
-> HandlerFor master Html
|
=> Status
|
||||||
-> HandlerFor master TypedContent
|
-> Text
|
||||||
|
-> m Html
|
||||||
|
-> m TypedContent
|
||||||
messageJsonStatus status msg html = selectRep $ do
|
messageJsonStatus status msg html = selectRep $ do
|
||||||
provideRep html
|
provideRep html
|
||||||
provideRep $ do
|
provideRep $ do
|
||||||
@ -340,9 +346,9 @@ provideJsonMessage :: Monad m => Text -> Writer.Writer (Endo [ProvidedRep m]) ()
|
|||||||
provideJsonMessage msg = provideRep $ return $ object ["message" .= msg]
|
provideJsonMessage msg = provideRep $ return $ object ["message" .= msg]
|
||||||
|
|
||||||
|
|
||||||
setCredsRedirect :: YesodAuth master
|
setCredsRedirect
|
||||||
=> Creds master -- ^ new credentials
|
:: Creds master -- ^ new credentials
|
||||||
-> HandlerFor master TypedContent
|
-> AuthHandler master TypedContent
|
||||||
setCredsRedirect creds = do
|
setCredsRedirect creds = do
|
||||||
y <- getYesod
|
y <- getYesod
|
||||||
auth <- authenticate creds
|
auth <- authenticate creds
|
||||||
@ -381,10 +387,9 @@ setCredsRedirect creds = do
|
|||||||
return $ renderAuthMessage master langs msg
|
return $ renderAuthMessage master langs msg
|
||||||
|
|
||||||
-- | Sets user credentials for the session after checking them with authentication backends.
|
-- | Sets user credentials for the session after checking them with authentication backends.
|
||||||
setCreds :: YesodAuth master
|
setCreds :: Bool -- ^ if HTTP redirects should be done
|
||||||
=> Bool -- ^ if HTTP redirects should be done
|
|
||||||
-> Creds master -- ^ new credentials
|
-> Creds master -- ^ new credentials
|
||||||
-> HandlerFor master ()
|
-> AuthHandler master ()
|
||||||
setCreds doRedirects creds =
|
setCreds doRedirects creds =
|
||||||
if doRedirects
|
if doRedirects
|
||||||
then void $ setCredsRedirect creds
|
then void $ setCredsRedirect creds
|
||||||
@ -394,10 +399,11 @@ setCreds doRedirects creds =
|
|||||||
_ -> return ()
|
_ -> return ()
|
||||||
|
|
||||||
-- | same as defaultLayoutJson, but uses authLayout
|
-- | same as defaultLayoutJson, but uses authLayout
|
||||||
authLayoutJson :: (YesodAuth site, ToJSON j)
|
authLayoutJson
|
||||||
=> WidgetFor site () -- ^ HTML
|
:: (ToJSON j, MonadAuthHandler master m)
|
||||||
-> HandlerFor site j -- ^ JSON
|
=> WidgetFor master () -- ^ HTML
|
||||||
-> HandlerFor site TypedContent
|
-> m j -- ^ JSON
|
||||||
|
-> m TypedContent
|
||||||
authLayoutJson w json = selectRep $ do
|
authLayoutJson w json = selectRep $ do
|
||||||
provideRep $ authLayout w
|
provideRep $ authLayout w
|
||||||
provideRep $ fmap toJSON json
|
provideRep $ fmap toJSON json
|
||||||
@ -405,18 +411,17 @@ authLayoutJson w json = selectRep $ do
|
|||||||
-- | Clears current user credentials for the session.
|
-- | Clears current user credentials for the session.
|
||||||
--
|
--
|
||||||
-- Since 1.1.7
|
-- Since 1.1.7
|
||||||
clearCreds :: (MonadHandler m, YesodAuth (HandlerSite m))
|
clearCreds :: Bool -- ^ if HTTP redirect to 'logoutDest' should be done
|
||||||
=> Bool -- ^ if HTTP redirect to 'logoutDest' should be done
|
-> AuthHandler master ()
|
||||||
-> m ()
|
|
||||||
clearCreds doRedirects = do
|
clearCreds doRedirects = do
|
||||||
y <- getYesod
|
y <- getYesod
|
||||||
liftHandler onLogout
|
onLogout
|
||||||
deleteSession credsKey
|
deleteSession credsKey
|
||||||
when doRedirects $ do
|
when doRedirects $ do
|
||||||
redirectUltDest $ logoutDest y
|
redirectUltDest $ logoutDest y
|
||||||
|
|
||||||
getCheckR :: AuthHandler master TypedContent
|
getCheckR :: AuthHandler master TypedContent
|
||||||
getCheckR = liftHandler $ do
|
getCheckR = do
|
||||||
creds <- maybeAuthId
|
creds <- maybeAuthId
|
||||||
authLayoutJson (do
|
authLayoutJson (do
|
||||||
setTitle "Authentication Status"
|
setTitle "Authentication Status"
|
||||||
@ -437,7 +442,7 @@ $nothing
|
|||||||
]
|
]
|
||||||
|
|
||||||
setUltDestReferer' :: AuthHandler master ()
|
setUltDestReferer' :: AuthHandler master ()
|
||||||
setUltDestReferer' = liftHandler $ do
|
setUltDestReferer' = do
|
||||||
master <- getYesod
|
master <- getYesod
|
||||||
when (redirectToReferer master) setUltDestReferer
|
when (redirectToReferer master) setUltDestReferer
|
||||||
|
|
||||||
@ -471,17 +476,16 @@ maybeAuth :: ( YesodAuthPersist master
|
|||||||
, Key val ~ AuthId master
|
, Key val ~ AuthId master
|
||||||
, PersistEntity val
|
, PersistEntity val
|
||||||
, Typeable val
|
, Typeable val
|
||||||
) => HandlerFor master (Maybe (Entity val))
|
) => AuthHandler master (Maybe (Entity val))
|
||||||
maybeAuth = runMaybeT $ do
|
maybeAuth = fmap (fmap (uncurry Entity)) maybeAuthPair
|
||||||
(aid, ae) <- MaybeT maybeAuthPair
|
|
||||||
return $ Entity aid ae
|
|
||||||
|
|
||||||
-- | Similar to 'maybeAuth', but doesn’t assume that you are using a
|
-- | Similar to 'maybeAuth', but doesn’t assume that you are using a
|
||||||
-- Persistent database.
|
-- Persistent database.
|
||||||
--
|
--
|
||||||
-- Since 1.4.0
|
-- Since 1.4.0
|
||||||
maybeAuthPair :: (YesodAuthPersist master, Typeable (AuthEntity master))
|
maybeAuthPair
|
||||||
=> HandlerFor master (Maybe (AuthId master, AuthEntity master))
|
:: (YesodAuthPersist master, Typeable (AuthEntity master))
|
||||||
|
=> AuthHandler master (Maybe (AuthId master, AuthEntity master))
|
||||||
maybeAuthPair = runMaybeT $ do
|
maybeAuthPair = runMaybeT $ do
|
||||||
aid <- MaybeT maybeAuthId
|
aid <- MaybeT maybeAuthId
|
||||||
ae <- MaybeT $ cachedAuth aid
|
ae <- MaybeT $ cachedAuth aid
|
||||||
@ -512,9 +516,8 @@ class (YesodAuth master, YesodPersist master) => YesodAuthPersist master where
|
|||||||
type AuthEntity master :: *
|
type AuthEntity master :: *
|
||||||
type AuthEntity master = KeyEntity (AuthId master)
|
type AuthEntity master = KeyEntity (AuthId master)
|
||||||
|
|
||||||
getAuthEntity :: AuthId master -> HandlerFor master (Maybe (AuthEntity master))
|
getAuthEntity :: AuthId master -> AuthHandler master (Maybe (AuthEntity master))
|
||||||
|
|
||||||
#if MIN_VERSION_persistent(2,5,0)
|
|
||||||
default getAuthEntity
|
default getAuthEntity
|
||||||
:: ( YesodPersistBackend master ~ backend
|
:: ( YesodPersistBackend master ~ backend
|
||||||
, PersistRecordBackend (AuthEntity master) backend
|
, PersistRecordBackend (AuthEntity master) backend
|
||||||
@ -522,16 +525,6 @@ class (YesodAuth master, YesodPersist master) => YesodAuthPersist master where
|
|||||||
, PersistStore backend
|
, PersistStore backend
|
||||||
)
|
)
|
||||||
=> AuthId master -> HandlerFor master (Maybe (AuthEntity master))
|
=> AuthId master -> HandlerFor master (Maybe (AuthEntity master))
|
||||||
#else
|
|
||||||
default getAuthEntity
|
|
||||||
:: ( YesodPersistBackend master
|
|
||||||
~ PersistEntityBackend (AuthEntity master)
|
|
||||||
, Key (AuthEntity master) ~ AuthId master
|
|
||||||
, PersistStore (YesodPersistBackend master)
|
|
||||||
, PersistEntity (AuthEntity master)
|
|
||||||
)
|
|
||||||
=> AuthId master -> HandlerFor master (Maybe (AuthEntity master))
|
|
||||||
#endif
|
|
||||||
getAuthEntity = runDB . get
|
getAuthEntity = runDB . get
|
||||||
|
|
||||||
|
|
||||||
@ -542,7 +535,7 @@ type instance KeyEntity (Key x) = x
|
|||||||
-- authenticated or responds with error 401 if this is an API client (expecting JSON).
|
-- authenticated or responds with error 401 if this is an API client (expecting JSON).
|
||||||
--
|
--
|
||||||
-- Since 1.1.0
|
-- Since 1.1.0
|
||||||
requireAuthId :: YesodAuth master => HandlerFor master (AuthId master)
|
requireAuthId :: AuthHandler master (AuthId master)
|
||||||
requireAuthId = maybeAuthId >>= maybe handleAuthLack return
|
requireAuthId = maybeAuthId >>= maybe handleAuthLack return
|
||||||
|
|
||||||
-- | Similar to 'maybeAuth', but redirects to a login page if user is not
|
-- | Similar to 'maybeAuth', but redirects to a login page if user is not
|
||||||
@ -554,23 +547,26 @@ requireAuth :: ( YesodAuthPersist master
|
|||||||
, Key val ~ AuthId master
|
, Key val ~ AuthId master
|
||||||
, PersistEntity val
|
, PersistEntity val
|
||||||
, Typeable val
|
, Typeable val
|
||||||
) => HandlerFor master (Entity val)
|
) => AuthHandler master (Entity val)
|
||||||
requireAuth = maybeAuth >>= maybe handleAuthLack return
|
requireAuth = maybeAuth >>= maybe handleAuthLack return
|
||||||
|
|
||||||
-- | Similar to 'requireAuth', but not tied to Persistent's 'Entity' type.
|
-- | Similar to 'requireAuth', but not tied to Persistent's 'Entity' type.
|
||||||
-- Instead, the 'AuthId' and 'AuthEntity' are returned in a tuple.
|
-- Instead, the 'AuthId' and 'AuthEntity' are returned in a tuple.
|
||||||
--
|
--
|
||||||
-- Since 1.4.0
|
-- Since 1.4.0
|
||||||
requireAuthPair :: (YesodAuthPersist master, Typeable (AuthEntity master))
|
requireAuthPair
|
||||||
=> HandlerFor master (AuthId master, AuthEntity master)
|
:: ( YesodAuthPersist master
|
||||||
|
, Typeable (AuthEntity master)
|
||||||
|
)
|
||||||
|
=> AuthHandler master (AuthId master, AuthEntity master)
|
||||||
requireAuthPair = maybeAuthPair >>= maybe handleAuthLack return
|
requireAuthPair = maybeAuthPair >>= maybe handleAuthLack return
|
||||||
|
|
||||||
handleAuthLack :: YesodAuth master => HandlerFor master a
|
handleAuthLack :: AuthHandler master a
|
||||||
handleAuthLack = do
|
handleAuthLack = do
|
||||||
aj <- acceptsJson
|
aj <- acceptsJson
|
||||||
if aj then notAuthenticated else redirectLogin
|
if aj then notAuthenticated else redirectLogin
|
||||||
|
|
||||||
redirectLogin :: YesodAuth master => HandlerFor master a
|
redirectLogin :: AuthHandler master a
|
||||||
redirectLogin = do
|
redirectLogin = do
|
||||||
y <- getYesod
|
y <- getYesod
|
||||||
when (redirectToCurrent y) setUltDestCurrent
|
when (redirectToCurrent y) setUltDestCurrent
|
||||||
@ -586,7 +582,7 @@ data AuthException = InvalidFacebookResponse
|
|||||||
instance Exception AuthException
|
instance Exception AuthException
|
||||||
|
|
||||||
-- FIXME HandlerSite m ~ SubHandlerSite m should be unnecessary
|
-- FIXME HandlerSite m ~ SubHandlerSite m should be unnecessary
|
||||||
instance (YesodAuth (HandlerSite m), HandlerSite m ~ SubHandlerSite m, MonadSubHandler m) => YesodSubDispatch Auth m where
|
instance (YesodAuth (HandlerSite m), HandlerSite m ~ SubHandlerSite m, MonadSubHandler m, MonadUnliftIO m) => YesodSubDispatch Auth m where
|
||||||
yesodSubDispatch = $(mkYesodSubDispatch resourcesAuth)
|
yesodSubDispatch = $(mkYesodSubDispatch resourcesAuth)
|
||||||
|
|
||||||
asHtml :: Html -> Html
|
asHtml :: Html -> Html
|
||||||
|
|||||||
@ -70,7 +70,6 @@ authBrowserId bis@BrowserIdSettings {..} = AuthPlugin
|
|||||||
, apDispatch = \m ps ->
|
, apDispatch = \m ps ->
|
||||||
case (m, ps) of
|
case (m, ps) of
|
||||||
("GET", [assertion]) -> do
|
("GET", [assertion]) -> do
|
||||||
master <- getYesod
|
|
||||||
audience <-
|
audience <-
|
||||||
case bisAudience of
|
case bisAudience of
|
||||||
Just a -> return a
|
Just a -> return a
|
||||||
@ -78,13 +77,14 @@ authBrowserId bis@BrowserIdSettings {..} = AuthPlugin
|
|||||||
r <- getUrlRender
|
r <- getUrlRender
|
||||||
tm <- getRouteToParent
|
tm <- getRouteToParent
|
||||||
return $ T.takeWhile (/= '/') $ stripScheme $ r $ tm LoginR
|
return $ T.takeWhile (/= '/') $ stripScheme $ r $ tm LoginR
|
||||||
memail <- liftHandler $ checkAssertion audience assertion (authHttpManager master)
|
manager <- authHttpManager
|
||||||
|
memail <- liftResourceT $ checkAssertion audience assertion manager
|
||||||
case memail of
|
case memail of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
$logErrorS "yesod-auth" "BrowserID assertion failure"
|
$logErrorS "yesod-auth" "BrowserID assertion failure"
|
||||||
tm <- getRouteToParent
|
tm <- getRouteToParent
|
||||||
liftHandler $ loginErrorMessage (tm LoginR) "BrowserID login error."
|
loginErrorMessage (tm LoginR) "BrowserID login error."
|
||||||
Just email -> liftHandler $ setCredsRedirect Creds
|
Just email -> setCredsRedirect Creds
|
||||||
{ credsPlugin = pid
|
{ credsPlugin = pid
|
||||||
, credsIdent = email
|
, credsIdent = email
|
||||||
, credsExtra = []
|
, credsExtra = []
|
||||||
@ -117,7 +117,7 @@ $newline never
|
|||||||
createOnClickOverride :: BrowserIdSettings
|
createOnClickOverride :: BrowserIdSettings
|
||||||
-> (Route Auth -> Route master)
|
-> (Route Auth -> Route master)
|
||||||
-> Maybe (Route master)
|
-> Maybe (Route master)
|
||||||
-> WidgetT master IO Text
|
-> WidgetFor master Text
|
||||||
createOnClickOverride BrowserIdSettings {..} toMaster mOnRegistration = do
|
createOnClickOverride BrowserIdSettings {..} toMaster mOnRegistration = do
|
||||||
unless bisLazyLoad $ addScriptRemote browserIdJs
|
unless bisLazyLoad $ addScriptRemote browserIdJs
|
||||||
onclick <- newIdent
|
onclick <- newIdent
|
||||||
@ -166,5 +166,5 @@ createOnClickOverride BrowserIdSettings {..} toMaster mOnRegistration = do
|
|||||||
-- name.
|
-- name.
|
||||||
createOnClick :: BrowserIdSettings
|
createOnClick :: BrowserIdSettings
|
||||||
-> (Route Auth -> Route master)
|
-> (Route Auth -> Route master)
|
||||||
-> WidgetT master IO Text
|
-> WidgetFor master Text
|
||||||
createOnClick bidSettings toMaster = createOnClickOverride bidSettings toMaster Nothing
|
createOnClick bidSettings toMaster = createOnClickOverride bidSettings toMaster Nothing
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
-- | Provides a dummy authentication module that simply lets a user specify
|
-- | Provides a dummy authentication module that simply lets a user specify
|
||||||
-- his/her identifier. This is not intended for real world use, just for
|
-- his/her identifier. This is not intended for real world use, just for
|
||||||
-- testing.
|
-- testing.
|
||||||
@ -16,7 +17,7 @@ authDummy :: YesodAuth m => AuthPlugin m
|
|||||||
authDummy =
|
authDummy =
|
||||||
AuthPlugin "dummy" dispatch login
|
AuthPlugin "dummy" dispatch login
|
||||||
where
|
where
|
||||||
dispatch "POST" [] = liftHandler $ do
|
dispatch "POST" [] = do
|
||||||
ident <- runInputPost $ ireq textField "ident"
|
ident <- runInputPost $ ireq textField "ident"
|
||||||
setCredsRedirect $ Creds "dummy" ident []
|
setCredsRedirect $ Creds "dummy" ident []
|
||||||
dispatch _ _ = notFound
|
dispatch _ _ = notFound
|
||||||
|
|||||||
@ -186,29 +186,29 @@ class ( YesodAuth site
|
|||||||
-- has not yet been verified.
|
-- has not yet been verified.
|
||||||
--
|
--
|
||||||
-- @since 1.1.0
|
-- @since 1.1.0
|
||||||
addUnverified :: Email -> VerKey -> HandlerT site IO (AuthEmailId site)
|
addUnverified :: Email -> VerKey -> AuthHandler site (AuthEmailId site)
|
||||||
|
|
||||||
-- | Send an email to the given address to verify ownership.
|
-- | Send an email to the given address to verify ownership.
|
||||||
--
|
--
|
||||||
-- @since 1.1.0
|
-- @since 1.1.0
|
||||||
sendVerifyEmail :: Email -> VerKey -> VerUrl -> HandlerT site IO ()
|
sendVerifyEmail :: Email -> VerKey -> VerUrl -> AuthHandler site ()
|
||||||
|
|
||||||
-- | Get the verification key for the given email ID.
|
-- | Get the verification key for the given email ID.
|
||||||
--
|
--
|
||||||
-- @since 1.1.0
|
-- @since 1.1.0
|
||||||
getVerifyKey :: AuthEmailId site -> HandlerT site IO (Maybe VerKey)
|
getVerifyKey :: AuthEmailId site -> AuthHandler site (Maybe VerKey)
|
||||||
|
|
||||||
-- | Set the verification key for the given email ID.
|
-- | Set the verification key for the given email ID.
|
||||||
--
|
--
|
||||||
-- @since 1.1.0
|
-- @since 1.1.0
|
||||||
setVerifyKey :: AuthEmailId site -> VerKey -> HandlerT site IO ()
|
setVerifyKey :: AuthEmailId site -> VerKey -> AuthHandler site ()
|
||||||
|
|
||||||
-- | Hash and salt a password
|
-- | Hash and salt a password
|
||||||
--
|
--
|
||||||
-- Default: 'saltPass'.
|
-- Default: 'saltPass'.
|
||||||
--
|
--
|
||||||
-- @since 1.4.20
|
-- @since 1.4.20
|
||||||
hashAndSaltPassword :: Text -> HandlerT site IO SaltedPass
|
hashAndSaltPassword :: Text -> AuthHandler site SaltedPass
|
||||||
hashAndSaltPassword = liftIO . saltPass
|
hashAndSaltPassword = liftIO . saltPass
|
||||||
|
|
||||||
-- | Verify a password matches the stored password for the given account.
|
-- | Verify a password matches the stored password for the given account.
|
||||||
@ -216,7 +216,7 @@ class ( YesodAuth site
|
|||||||
-- Default: Fetch a password with 'getPassword' and match using 'Yesod.Auth.Util.PasswordStore.verifyPassword'.
|
-- Default: Fetch a password with 'getPassword' and match using 'Yesod.Auth.Util.PasswordStore.verifyPassword'.
|
||||||
--
|
--
|
||||||
-- @since 1.4.20
|
-- @since 1.4.20
|
||||||
verifyPassword :: Text -> SaltedPass -> HandlerT site IO Bool
|
verifyPassword :: Text -> SaltedPass -> AuthHandler site Bool
|
||||||
verifyPassword plain salted = return $ isValidPass plain salted
|
verifyPassword plain salted = return $ isValidPass plain salted
|
||||||
|
|
||||||
-- | Verify the email address on the given account.
|
-- | Verify the email address on the given account.
|
||||||
@ -228,28 +228,28 @@ class ( YesodAuth site
|
|||||||
-- See <https://github.com/yesodweb/yesod/issues/1222>.
|
-- See <https://github.com/yesodweb/yesod/issues/1222>.
|
||||||
--
|
--
|
||||||
-- @since 1.1.0
|
-- @since 1.1.0
|
||||||
verifyAccount :: AuthEmailId site -> HandlerT site IO (Maybe (AuthId site))
|
verifyAccount :: AuthEmailId site -> AuthHandler site (Maybe (AuthId site))
|
||||||
|
|
||||||
-- | Get the salted password for the given account.
|
-- | Get the salted password for the given account.
|
||||||
--
|
--
|
||||||
-- @since 1.1.0
|
-- @since 1.1.0
|
||||||
getPassword :: AuthId site -> HandlerT site IO (Maybe SaltedPass)
|
getPassword :: AuthId site -> AuthHandler site (Maybe SaltedPass)
|
||||||
|
|
||||||
-- | Set the salted password for the given account.
|
-- | Set the salted password for the given account.
|
||||||
--
|
--
|
||||||
-- @since 1.1.0
|
-- @since 1.1.0
|
||||||
setPassword :: AuthId site -> SaltedPass -> HandlerT site IO ()
|
setPassword :: AuthId site -> SaltedPass -> AuthHandler site ()
|
||||||
|
|
||||||
-- | Get the credentials for the given @Identifier@, which may be either an
|
-- | Get the credentials for the given @Identifier@, which may be either an
|
||||||
-- email address or some other identification (e.g., username).
|
-- email address or some other identification (e.g., username).
|
||||||
--
|
--
|
||||||
-- @since 1.2.0
|
-- @since 1.2.0
|
||||||
getEmailCreds :: Identifier -> HandlerT site IO (Maybe (EmailCreds site))
|
getEmailCreds :: Identifier -> AuthHandler site (Maybe (EmailCreds site))
|
||||||
|
|
||||||
-- | Get the email address for the given email ID.
|
-- | Get the email address for the given email ID.
|
||||||
--
|
--
|
||||||
-- @since 1.1.0
|
-- @since 1.1.0
|
||||||
getEmail :: AuthEmailId site -> HandlerT site IO (Maybe Email)
|
getEmail :: AuthEmailId site -> AuthHandler site (Maybe Email)
|
||||||
|
|
||||||
-- | Generate a random alphanumeric string.
|
-- | Generate a random alphanumeric string.
|
||||||
--
|
--
|
||||||
@ -268,7 +268,7 @@ class ( YesodAuth site
|
|||||||
-- Default: if the user logged in via an email link do not require a password.
|
-- Default: if the user logged in via an email link do not require a password.
|
||||||
--
|
--
|
||||||
-- @since 1.2.1
|
-- @since 1.2.1
|
||||||
needOldPassword :: AuthId site -> HandlerT site IO Bool
|
needOldPassword :: AuthId site -> AuthHandler site Bool
|
||||||
needOldPassword aid' = do
|
needOldPassword aid' = do
|
||||||
mkey <- lookupSession loginLinkKey
|
mkey <- lookupSession loginLinkKey
|
||||||
case mkey >>= readMay . TS.unpack of
|
case mkey >>= readMay . TS.unpack of
|
||||||
@ -280,7 +280,7 @@ class ( YesodAuth site
|
|||||||
-- | Check that the given plain-text password meets minimum security standards.
|
-- | Check that the given plain-text password meets minimum security standards.
|
||||||
--
|
--
|
||||||
-- Default: password is at least three characters.
|
-- Default: password is at least three characters.
|
||||||
checkPasswordSecurity :: AuthId site -> Text -> HandlerT site IO (Either Text ())
|
checkPasswordSecurity :: AuthId site -> Text -> AuthHandler site (Either Text ())
|
||||||
checkPasswordSecurity _ x
|
checkPasswordSecurity _ x
|
||||||
| TS.length x >= 3 = return $ Right ()
|
| TS.length x >= 3 = return $ Right ()
|
||||||
| otherwise = return $ Left "Password must be at least three characters"
|
| otherwise = return $ Left "Password must be at least three characters"
|
||||||
@ -288,7 +288,7 @@ class ( YesodAuth site
|
|||||||
-- | Response after sending a confirmation email.
|
-- | Response after sending a confirmation email.
|
||||||
--
|
--
|
||||||
-- @since 1.2.2
|
-- @since 1.2.2
|
||||||
confirmationEmailSentResponse :: Text -> HandlerT site IO TypedContent
|
confirmationEmailSentResponse :: Text -> AuthHandler site TypedContent
|
||||||
confirmationEmailSentResponse identifier = do
|
confirmationEmailSentResponse identifier = do
|
||||||
mr <- getMessageRender
|
mr <- getMessageRender
|
||||||
selectRep $ do
|
selectRep $ do
|
||||||
@ -314,7 +314,7 @@ class ( YesodAuth site
|
|||||||
-- Default: 'defaultEmailLoginHandler'.
|
-- Default: 'defaultEmailLoginHandler'.
|
||||||
--
|
--
|
||||||
-- @since 1.4.17
|
-- @since 1.4.17
|
||||||
emailLoginHandler :: (Route Auth -> Route site) -> WidgetT site IO ()
|
emailLoginHandler :: (Route Auth -> Route site) -> WidgetFor site ()
|
||||||
emailLoginHandler = defaultEmailLoginHandler
|
emailLoginHandler = defaultEmailLoginHandler
|
||||||
|
|
||||||
|
|
||||||
@ -377,9 +377,12 @@ getRegisterR = registerHandler
|
|||||||
-- | Default implementation of 'emailLoginHandler'.
|
-- | Default implementation of 'emailLoginHandler'.
|
||||||
--
|
--
|
||||||
-- @since 1.4.17
|
-- @since 1.4.17
|
||||||
defaultEmailLoginHandler :: YesodAuthEmail master => (Route Auth -> Route master) -> WidgetT master IO ()
|
defaultEmailLoginHandler
|
||||||
|
:: YesodAuthEmail master
|
||||||
|
=> (Route Auth -> Route master)
|
||||||
|
-> WidgetFor master ()
|
||||||
defaultEmailLoginHandler toParent = do
|
defaultEmailLoginHandler toParent = do
|
||||||
(widget, enctype) <- liftWidgetT $ generateFormPost loginForm
|
(widget, enctype) <- generateFormPost loginForm
|
||||||
|
|
||||||
[whamlet|
|
[whamlet|
|
||||||
<form method="post" action="@{toParent loginR}", enctype=#{enctype}>
|
<form method="post" action="@{toParent loginR}", enctype=#{enctype}>
|
||||||
@ -439,9 +442,9 @@ defaultEmailLoginHandler toParent = do
|
|||||||
-- @since 1.2.6
|
-- @since 1.2.6
|
||||||
defaultRegisterHandler :: YesodAuthEmail master => AuthHandler master Html
|
defaultRegisterHandler :: YesodAuthEmail master => AuthHandler master Html
|
||||||
defaultRegisterHandler = do
|
defaultRegisterHandler = do
|
||||||
(widget, enctype) <- lift $ generateFormPost registrationForm
|
(widget, enctype) <- generateFormPost registrationForm
|
||||||
toParentRoute <- getRouteToParent
|
toParentRoute <- getRouteToParent
|
||||||
lift $ authLayout $ do
|
authLayout $ do
|
||||||
setTitleI Msg.RegisterLong
|
setTitleI Msg.RegisterLong
|
||||||
[whamlet|
|
[whamlet|
|
||||||
<p>_{Msg.EnterEmail}
|
<p>_{Msg.EnterEmail}
|
||||||
@ -482,12 +485,12 @@ registerHelper :: YesodAuthEmail master
|
|||||||
-> Route Auth
|
-> Route Auth
|
||||||
-> AuthHandler master TypedContent
|
-> AuthHandler master TypedContent
|
||||||
registerHelper allowUsername dest = do
|
registerHelper allowUsername dest = do
|
||||||
y <- lift getYesod
|
y <- getYesod
|
||||||
checkCsrfHeaderOrParam defaultCsrfHeaderName defaultCsrfParamName
|
checkCsrfHeaderOrParam defaultCsrfHeaderName defaultCsrfParamName
|
||||||
pidentifier <- lookupPostParam "email"
|
pidentifier <- lookupPostParam "email"
|
||||||
midentifier <- case pidentifier of
|
midentifier <- case pidentifier of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
(jidentifier :: Result Value) <- lift parseCheckJsonBody
|
(jidentifier :: Result Value) <- parseCheckJsonBody
|
||||||
case jidentifier of
|
case jidentifier of
|
||||||
Error _ -> return Nothing
|
Error _ -> return Nothing
|
||||||
Success val -> return $ parseMaybe parseEmail val
|
Success val -> return $ parseMaybe parseEmail val
|
||||||
@ -502,28 +505,29 @@ registerHelper allowUsername dest = do
|
|||||||
case eidentifier of
|
case eidentifier of
|
||||||
Left route -> loginErrorMessageI dest route
|
Left route -> loginErrorMessageI dest route
|
||||||
Right identifier -> do
|
Right identifier -> do
|
||||||
mecreds <- lift $ getEmailCreds identifier
|
mecreds <- getEmailCreds identifier
|
||||||
registerCreds <-
|
registerCreds <-
|
||||||
case mecreds of
|
case mecreds of
|
||||||
Just (EmailCreds lid _ _ (Just key) email) -> return $ Just (lid, key, email)
|
Just (EmailCreds lid _ _ (Just key) email) -> return $ Just (lid, key, email)
|
||||||
Just (EmailCreds lid _ _ Nothing email) -> do
|
Just (EmailCreds lid _ _ Nothing email) -> do
|
||||||
key <- liftIO $ randomKey y
|
key <- liftIO $ randomKey y
|
||||||
lift $ setVerifyKey lid key
|
setVerifyKey lid key
|
||||||
return $ Just (lid, key, email)
|
return $ Just (lid, key, email)
|
||||||
Nothing
|
Nothing
|
||||||
| allowUsername -> return Nothing
|
| allowUsername -> return Nothing
|
||||||
| otherwise -> do
|
| otherwise -> do
|
||||||
key <- liftIO $ randomKey y
|
key <- liftIO $ randomKey y
|
||||||
lid <- lift $ addUnverified identifier key
|
lid <- addUnverified identifier key
|
||||||
return $ Just (lid, key, identifier)
|
return $ Just (lid, key, identifier)
|
||||||
|
|
||||||
case registerCreds of
|
case registerCreds of
|
||||||
Nothing -> loginErrorMessageI dest (Msg.IdentifierNotFound identifier)
|
Nothing -> loginErrorMessageI dest (Msg.IdentifierNotFound identifier)
|
||||||
Just (lid, verKey, email) -> do
|
Just (lid, verKey, email) -> do
|
||||||
render <- getUrlRender
|
render <- getUrlRender
|
||||||
let verUrl = render $ verifyR (toPathPiece lid) verKey
|
tp <- getRouteToParent
|
||||||
lift $ sendVerifyEmail email verKey verUrl
|
let verUrl = render $ tp $ verifyR (toPathPiece lid) verKey
|
||||||
lift $ confirmationEmailSentResponse identifier
|
sendVerifyEmail email verKey verUrl
|
||||||
|
confirmationEmailSentResponse identifier
|
||||||
|
|
||||||
postRegisterR :: YesodAuthEmail master => AuthHandler master TypedContent
|
postRegisterR :: YesodAuthEmail master => AuthHandler master TypedContent
|
||||||
postRegisterR = registerHelper False registerR
|
postRegisterR = registerHelper False registerR
|
||||||
@ -536,9 +540,9 @@ getForgotPasswordR = forgotPasswordHandler
|
|||||||
-- @since 1.2.6
|
-- @since 1.2.6
|
||||||
defaultForgotPasswordHandler :: YesodAuthEmail master => AuthHandler master Html
|
defaultForgotPasswordHandler :: YesodAuthEmail master => AuthHandler master Html
|
||||||
defaultForgotPasswordHandler = do
|
defaultForgotPasswordHandler = do
|
||||||
(widget, enctype) <- lift $ generateFormPost forgotPasswordForm
|
(widget, enctype) <- generateFormPost forgotPasswordForm
|
||||||
toParent <- getRouteToParent
|
toParent <- getRouteToParent
|
||||||
lift $ authLayout $ do
|
authLayout $ do
|
||||||
setTitleI Msg.PasswordResetTitle
|
setTitleI Msg.PasswordResetTitle
|
||||||
[whamlet|
|
[whamlet|
|
||||||
<p>_{Msg.PasswordResetPrompt}
|
<p>_{Msg.PasswordResetPrompt}
|
||||||
@ -577,27 +581,28 @@ getVerifyR :: YesodAuthEmail site
|
|||||||
-> Text
|
-> Text
|
||||||
-> AuthHandler site TypedContent
|
-> AuthHandler site TypedContent
|
||||||
getVerifyR lid key = do
|
getVerifyR lid key = do
|
||||||
realKey <- lift $ getVerifyKey lid
|
realKey <- getVerifyKey lid
|
||||||
memail <- lift $ getEmail lid
|
memail <- getEmail lid
|
||||||
mr <- lift getMessageRender
|
mr <- getMessageRender
|
||||||
case (realKey == Just key, memail) of
|
case (realKey == Just key, memail) of
|
||||||
(True, Just email) -> do
|
(True, Just email) -> do
|
||||||
muid <- lift $ verifyAccount lid
|
muid <- verifyAccount lid
|
||||||
case muid of
|
case muid of
|
||||||
Nothing -> invalidKey mr
|
Nothing -> invalidKey mr
|
||||||
Just uid -> do
|
Just uid -> do
|
||||||
lift $ setCreds False $ Creds "email-verify" email [("verifiedEmail", email)] -- FIXME uid?
|
setCreds False $ Creds "email-verify" email [("verifiedEmail", email)] -- FIXME uid?
|
||||||
lift $ setLoginLinkKey uid
|
setLoginLinkKey uid
|
||||||
let msgAv = Msg.AddressVerified
|
let msgAv = Msg.AddressVerified
|
||||||
selectRep $ do
|
selectRep $ do
|
||||||
provideRep $ do
|
provideRep $ do
|
||||||
lift $ addMessageI "success" msgAv
|
addMessageI "success" msgAv
|
||||||
fmap asHtml $ redirect setpassR
|
tp <- getRouteToParent
|
||||||
|
fmap asHtml $ redirect $ tp setpassR
|
||||||
provideJsonMessage $ mr msgAv
|
provideJsonMessage $ mr msgAv
|
||||||
_ -> invalidKey mr
|
_ -> invalidKey mr
|
||||||
where
|
where
|
||||||
msgIk = Msg.InvalidKey
|
msgIk = Msg.InvalidKey
|
||||||
invalidKey mr = messageJson401 (mr msgIk) $ lift $ authLayout $ do
|
invalidKey mr = messageJson401 (mr msgIk) $ authLayout $ do
|
||||||
setTitleI msgIk
|
setTitleI msgIk
|
||||||
[whamlet|
|
[whamlet|
|
||||||
$newline never
|
$newline never
|
||||||
@ -614,14 +619,14 @@ parseCreds = withObject "creds" (\obj -> do
|
|||||||
|
|
||||||
postLoginR :: YesodAuthEmail master => AuthHandler master TypedContent
|
postLoginR :: YesodAuthEmail master => AuthHandler master TypedContent
|
||||||
postLoginR = do
|
postLoginR = do
|
||||||
result <- lift $ runInputPostResult $ (,)
|
result <- runInputPostResult $ (,)
|
||||||
<$> ireq textField "email"
|
<$> ireq textField "email"
|
||||||
<*> ireq textField "password"
|
<*> ireq textField "password"
|
||||||
|
|
||||||
midentifier <- case result of
|
midentifier <- case result of
|
||||||
FormSuccess (iden, pass) -> return $ Just (iden, pass)
|
FormSuccess (iden, pass) -> return $ Just (iden, pass)
|
||||||
_ -> do
|
_ -> do
|
||||||
(creds :: Result Value) <- lift parseCheckJsonBody
|
(creds :: Result Value) <- parseCheckJsonBody
|
||||||
case creds of
|
case creds of
|
||||||
Error _ -> return Nothing
|
Error _ -> return Nothing
|
||||||
Success val -> return $ parseMaybe parseCreds val
|
Success val -> return $ parseMaybe parseCreds val
|
||||||
@ -629,18 +634,18 @@ postLoginR = do
|
|||||||
case midentifier of
|
case midentifier of
|
||||||
Nothing -> loginErrorMessageI LoginR Msg.NoIdentifierProvided
|
Nothing -> loginErrorMessageI LoginR Msg.NoIdentifierProvided
|
||||||
Just (identifier, pass) -> do
|
Just (identifier, pass) -> do
|
||||||
mecreds <- lift $ getEmailCreds identifier
|
mecreds <- getEmailCreds identifier
|
||||||
maid <-
|
maid <-
|
||||||
case ( mecreds >>= emailCredsAuthId
|
case ( mecreds >>= emailCredsAuthId
|
||||||
, emailCredsEmail <$> mecreds
|
, emailCredsEmail <$> mecreds
|
||||||
, emailCredsStatus <$> mecreds
|
, emailCredsStatus <$> mecreds
|
||||||
) of
|
) of
|
||||||
(Just aid, Just email', Just True) -> do
|
(Just aid, Just email', Just True) -> do
|
||||||
mrealpass <- lift $ getPassword aid
|
mrealpass <- getPassword aid
|
||||||
case mrealpass of
|
case mrealpass of
|
||||||
Nothing -> return Nothing
|
Nothing -> return Nothing
|
||||||
Just realpass -> do
|
Just realpass -> do
|
||||||
passValid <- lift $ verifyPassword pass realpass
|
passValid <- verifyPassword pass realpass
|
||||||
return $ if passValid
|
return $ if passValid
|
||||||
then Just email'
|
then Just email'
|
||||||
else Nothing
|
else Nothing
|
||||||
@ -648,7 +653,7 @@ postLoginR = do
|
|||||||
let isEmail = Text.Email.Validate.isValid $ encodeUtf8 identifier
|
let isEmail = Text.Email.Validate.isValid $ encodeUtf8 identifier
|
||||||
case maid of
|
case maid of
|
||||||
Just email' ->
|
Just email' ->
|
||||||
lift $ setCredsRedirect $ Creds
|
setCredsRedirect $ Creds
|
||||||
(if isEmail then "email" else "username")
|
(if isEmail then "email" else "username")
|
||||||
email'
|
email'
|
||||||
[("verifiedEmail", email')]
|
[("verifiedEmail", email')]
|
||||||
@ -660,11 +665,11 @@ postLoginR = do
|
|||||||
|
|
||||||
getPasswordR :: YesodAuthEmail master => AuthHandler master TypedContent
|
getPasswordR :: YesodAuthEmail master => AuthHandler master TypedContent
|
||||||
getPasswordR = do
|
getPasswordR = do
|
||||||
maid <- lift maybeAuthId
|
maid <- maybeAuthId
|
||||||
case maid of
|
case maid of
|
||||||
Nothing -> loginErrorMessageI LoginR Msg.BadSetPass
|
Nothing -> loginErrorMessageI LoginR Msg.BadSetPass
|
||||||
Just _ -> do
|
Just _ -> do
|
||||||
needOld <- maybe (return True) (lift . needOldPassword) maid
|
needOld <- maybe (return True) needOldPassword maid
|
||||||
setPasswordHandler needOld
|
setPasswordHandler needOld
|
||||||
|
|
||||||
-- | Default implementation of 'setPasswordHandler'.
|
-- | Default implementation of 'setPasswordHandler'.
|
||||||
@ -672,12 +677,12 @@ getPasswordR = do
|
|||||||
-- @since 1.2.6
|
-- @since 1.2.6
|
||||||
defaultSetPasswordHandler :: YesodAuthEmail master => Bool -> AuthHandler master TypedContent
|
defaultSetPasswordHandler :: YesodAuthEmail master => Bool -> AuthHandler master TypedContent
|
||||||
defaultSetPasswordHandler needOld = do
|
defaultSetPasswordHandler needOld = do
|
||||||
messageRender <- lift getMessageRender
|
messageRender <- getMessageRender
|
||||||
toParent <- getRouteToParent
|
toParent <- getRouteToParent
|
||||||
selectRep $ do
|
selectRep $ do
|
||||||
provideJsonMessage $ messageRender Msg.SetPass
|
provideJsonMessage $ messageRender Msg.SetPass
|
||||||
provideRep $ lift $ authLayout $ do
|
provideRep $ authLayout $ do
|
||||||
(widget, enctype) <- liftWidgetT $ generateFormPost setPasswordForm
|
(widget, enctype) <- generateFormPost setPasswordForm
|
||||||
setTitleI Msg.SetPassTitle
|
setTitleI Msg.SetPassTitle
|
||||||
[whamlet|
|
[whamlet|
|
||||||
<h3>_{Msg.SetPass}
|
<h3>_{Msg.SetPass}
|
||||||
@ -751,8 +756,8 @@ parsePassword = withObject "password" (\obj -> do
|
|||||||
|
|
||||||
postPasswordR :: YesodAuthEmail master => AuthHandler master TypedContent
|
postPasswordR :: YesodAuthEmail master => AuthHandler master TypedContent
|
||||||
postPasswordR = do
|
postPasswordR = do
|
||||||
maid <- lift maybeAuthId
|
maid <- maybeAuthId
|
||||||
(creds :: Result Value) <- lift parseCheckJsonBody
|
(creds :: Result Value) <- parseCheckJsonBody
|
||||||
let jcreds = case creds of
|
let jcreds = case creds of
|
||||||
Error _ -> Nothing
|
Error _ -> Nothing
|
||||||
Success val -> parseMaybe parsePassword val
|
Success val -> parseMaybe parsePassword val
|
||||||
@ -761,26 +766,26 @@ postPasswordR = do
|
|||||||
Nothing -> loginErrorMessageI LoginR Msg.BadSetPass
|
Nothing -> loginErrorMessageI LoginR Msg.BadSetPass
|
||||||
Just aid -> do
|
Just aid -> do
|
||||||
tm <- getRouteToParent
|
tm <- getRouteToParent
|
||||||
needOld <- lift $ needOldPassword aid
|
needOld <- needOldPassword aid
|
||||||
if not needOld then confirmPassword aid tm jcreds else do
|
if not needOld then confirmPassword aid tm jcreds else do
|
||||||
res <- lift $ runInputPostResult $ ireq textField "current"
|
res <- runInputPostResult $ ireq textField "current"
|
||||||
let fcurrent = case res of
|
let fcurrent = case res of
|
||||||
FormSuccess currentPass -> Just currentPass
|
FormSuccess currentPass -> Just currentPass
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
let current = if doJsonParsing
|
let current = if doJsonParsing
|
||||||
then getThird jcreds
|
then getThird jcreds
|
||||||
else fcurrent
|
else fcurrent
|
||||||
mrealpass <- lift $ getPassword aid
|
mrealpass <- getPassword aid
|
||||||
case (mrealpass, current) of
|
case (mrealpass, current) of
|
||||||
(Nothing, _) ->
|
(Nothing, _) ->
|
||||||
liftHandler $ loginErrorMessage (tm setpassR) "You do not currently have a password set on your account"
|
loginErrorMessage (tm setpassR) "You do not currently have a password set on your account"
|
||||||
(_, Nothing) ->
|
(_, Nothing) ->
|
||||||
loginErrorMessageI LoginR Msg.BadSetPass
|
loginErrorMessageI LoginR Msg.BadSetPass
|
||||||
(Just realpass, Just current') -> do
|
(Just realpass, Just current') -> do
|
||||||
passValid <- liftHandler $ verifyPassword current' realpass
|
passValid <- verifyPassword current' realpass
|
||||||
if passValid
|
if passValid
|
||||||
then confirmPassword aid tm jcreds
|
then confirmPassword aid tm jcreds
|
||||||
else liftHandler $ loginErrorMessage (tm setpassR) "Invalid current password, please try again"
|
else loginErrorMessage (tm setpassR) "Invalid current password, please try again"
|
||||||
|
|
||||||
where
|
where
|
||||||
msgOk = Msg.PassUpdated
|
msgOk = Msg.PassUpdated
|
||||||
@ -789,7 +794,7 @@ postPasswordR = do
|
|||||||
getNewConfirm (Just (a,b,_)) = Just (a,b)
|
getNewConfirm (Just (a,b,_)) = Just (a,b)
|
||||||
getNewConfirm _ = Nothing
|
getNewConfirm _ = Nothing
|
||||||
confirmPassword aid tm jcreds = do
|
confirmPassword aid tm jcreds = do
|
||||||
res <- lift $ runInputPostResult $ (,)
|
res <- runInputPostResult $ (,)
|
||||||
<$> ireq textField "new"
|
<$> ireq textField "new"
|
||||||
<*> ireq textField "confirm"
|
<*> ireq textField "confirm"
|
||||||
let creds = if (isJust jcreds)
|
let creds = if (isJust jcreds)
|
||||||
@ -803,21 +808,21 @@ postPasswordR = do
|
|||||||
if new /= confirm
|
if new /= confirm
|
||||||
then loginErrorMessageI setpassR Msg.PassMismatch
|
then loginErrorMessageI setpassR Msg.PassMismatch
|
||||||
else do
|
else do
|
||||||
isSecure <- lift $ checkPasswordSecurity aid new
|
isSecure <- checkPasswordSecurity aid new
|
||||||
case isSecure of
|
case isSecure of
|
||||||
Left e -> lift $ loginErrorMessage (tm setpassR) e
|
Left e -> loginErrorMessage (tm setpassR) e
|
||||||
Right () -> do
|
Right () -> do
|
||||||
salted <- lift $ hashAndSaltPassword new
|
salted <- hashAndSaltPassword new
|
||||||
y <- lift $ do
|
y <- do
|
||||||
setPassword aid salted
|
setPassword aid salted
|
||||||
deleteSession loginLinkKey
|
deleteSession loginLinkKey
|
||||||
addMessageI "success" msgOk
|
addMessageI "success" msgOk
|
||||||
getYesod
|
getYesod
|
||||||
|
|
||||||
mr <- lift getMessageRender
|
mr <- getMessageRender
|
||||||
selectRep $ do
|
selectRep $ do
|
||||||
provideRep $
|
provideRep $
|
||||||
fmap asHtml $ lift $ redirect $ afterPasswordRoute y
|
fmap asHtml $ redirect $ afterPasswordRoute y
|
||||||
provideJsonMessage (mr msgOk)
|
provideJsonMessage (mr msgOk)
|
||||||
|
|
||||||
saltLength :: Int
|
saltLength :: Int
|
||||||
|
|||||||
@ -1,89 +0,0 @@
|
|||||||
{-# LANGUAGE QuasiQuotes #-}
|
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
{-# LANGUAGE CPP #-}
|
|
||||||
{-# LANGUAGE RankNTypes #-}
|
|
||||||
-- | Use an email address as an identifier via Google's OpenID login system.
|
|
||||||
--
|
|
||||||
-- This backend will not use the OpenID identifier at all. It only uses OpenID
|
|
||||||
-- as a login system. By using this plugin, you are trusting Google to validate
|
|
||||||
-- an email address, and requiring users to have a Google account. On the plus
|
|
||||||
-- side, you get to use email addresses as the identifier, many users have
|
|
||||||
-- existing Google accounts, the login system has been long tested (as opposed
|
|
||||||
-- to BrowserID), and it requires no credential managing or setup (as opposed
|
|
||||||
-- to Email).
|
|
||||||
module Yesod.Auth.GoogleEmail
|
|
||||||
{-# DEPRECATED "Google no longer provides OpenID support, please use Yesod.Auth.GoogleEmail2" #-}
|
|
||||||
( authGoogleEmail
|
|
||||||
, forwardUrl
|
|
||||||
) where
|
|
||||||
|
|
||||||
import Yesod.Auth
|
|
||||||
import qualified Web.Authenticate.OpenId as OpenId
|
|
||||||
|
|
||||||
import Yesod.Core
|
|
||||||
import Data.Text (Text)
|
|
||||||
import qualified Yesod.Auth.Message as Msg
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import Control.Exception.Lifted (try, SomeException)
|
|
||||||
|
|
||||||
pid :: Text
|
|
||||||
pid = "googleemail"
|
|
||||||
|
|
||||||
forwardUrl :: AuthRoute
|
|
||||||
forwardUrl = PluginR pid ["forward"]
|
|
||||||
|
|
||||||
googleIdent :: Text
|
|
||||||
googleIdent = "https://www.google.com/accounts/o8/id"
|
|
||||||
|
|
||||||
authGoogleEmail :: YesodAuth m => AuthPlugin m
|
|
||||||
authGoogleEmail =
|
|
||||||
AuthPlugin pid dispatch login
|
|
||||||
where
|
|
||||||
complete = PluginR pid ["complete"]
|
|
||||||
login tm =
|
|
||||||
[whamlet|<a href=@{tm forwardUrl}>_{Msg.LoginGoogle}|]
|
|
||||||
dispatch "GET" ["forward"] = do
|
|
||||||
render <- getUrlRender
|
|
||||||
let complete' = render complete
|
|
||||||
master <- lift getYesod
|
|
||||||
eres <- lift $ try $ OpenId.getForwardUrl googleIdent complete' Nothing
|
|
||||||
[ ("openid.ax.type.email", "http://schema.openid.net/contact/email")
|
|
||||||
, ("openid.ns.ax", "http://openid.net/srv/ax/1.0")
|
|
||||||
, ("openid.ns.ax.required", "email")
|
|
||||||
, ("openid.ax.mode", "fetch_request")
|
|
||||||
, ("openid.ax.required", "email")
|
|
||||||
, ("openid.ui.icon", "true")
|
|
||||||
] (authHttpManager master)
|
|
||||||
either
|
|
||||||
(\err -> do
|
|
||||||
tm <- getRouteToParent
|
|
||||||
lift $ loginErrorMessage (tm LoginR) $ T.pack $ show (err :: SomeException))
|
|
||||||
redirect
|
|
||||||
eres
|
|
||||||
dispatch "GET" ["complete", ""] = dispatch "GET" ["complete"] -- compatibility issues
|
|
||||||
dispatch "GET" ["complete"] = do
|
|
||||||
rr <- getRequest
|
|
||||||
completeHelper $ reqGetParams rr
|
|
||||||
dispatch "POST" ["complete", ""] = dispatch "POST" ["complete"] -- compatibility issues
|
|
||||||
dispatch "POST" ["complete"] = do
|
|
||||||
(posts, _) <- runRequestBody
|
|
||||||
completeHelper posts
|
|
||||||
dispatch _ _ = notFound
|
|
||||||
|
|
||||||
completeHelper :: [(Text, Text)] -> AuthHandler master TypedContent
|
|
||||||
completeHelper gets' = do
|
|
||||||
master <- lift getYesod
|
|
||||||
eres <- lift $ try $ OpenId.authenticateClaimed gets' (authHttpManager master)
|
|
||||||
tm <- getRouteToParent
|
|
||||||
either (onFailure tm) (onSuccess tm) eres
|
|
||||||
where
|
|
||||||
onFailure tm err =
|
|
||||||
lift $ loginErrorMessage (tm LoginR) $ T.pack $ show (err :: SomeException)
|
|
||||||
onSuccess tm oir = do
|
|
||||||
let OpenId.Identifier ident = OpenId.oirOpLocal oir
|
|
||||||
memail <- lookupGetParam "openid.ext1.value.email"
|
|
||||||
case (memail, "https://www.google.com/accounts/o8/id" `T.isPrefixOf` ident) of
|
|
||||||
(Just email, True) -> lift $ setCredsRedirect $ Creds pid email []
|
|
||||||
(_, False) -> lift $ loginErrorMessage (tm LoginR) "Only Google login is supported"
|
|
||||||
(Nothing, _) -> lift $ loginErrorMessage (tm LoginR) "No email address provided"
|
|
||||||
@ -2,6 +2,8 @@
|
|||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
{-# LANGUAGE RankNTypes #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
-- | Use an email address as an identifier via Google's login system.
|
-- | Use an email address as an identifier via Google's login system.
|
||||||
--
|
--
|
||||||
-- Note that this is a replacement for "Yesod.Auth.GoogleEmail", which depends
|
-- Note that this is a replacement for "Yesod.Auth.GoogleEmail", which depends
|
||||||
@ -54,12 +56,12 @@ import Yesod.Auth (Auth, AuthPlugin (AuthPlugin),
|
|||||||
AuthRoute, Creds (Creds),
|
AuthRoute, Creds (Creds),
|
||||||
Route (PluginR), YesodAuth,
|
Route (PluginR), YesodAuth,
|
||||||
runHttpRequest, setCredsRedirect,
|
runHttpRequest, setCredsRedirect,
|
||||||
logoutDest)
|
logoutDest, AuthHandler)
|
||||||
import qualified Yesod.Auth.Message as Msg
|
import qualified Yesod.Auth.Message as Msg
|
||||||
import Yesod.Core (HandlerSite, HandlerT, MonadHandler,
|
import Yesod.Core (HandlerSite, MonadHandler,
|
||||||
TypedContent, getRouteToParent,
|
TypedContent, getRouteToParent,
|
||||||
getUrlRender, invalidArgs,
|
getUrlRender, invalidArgs,
|
||||||
lift, liftIO, lookupGetParam,
|
liftIO, lookupGetParam,
|
||||||
lookupSession, notFound, redirect,
|
lookupSession, notFound, redirect,
|
||||||
setSession, whamlet, (.:),
|
setSession, whamlet, (.:),
|
||||||
addMessage, getYesod,
|
addMessage, getYesod,
|
||||||
@ -187,10 +189,10 @@ authPlugin storeToken clientID clientSecret =
|
|||||||
dispatch :: YesodAuth site
|
dispatch :: YesodAuth site
|
||||||
=> Text
|
=> Text
|
||||||
-> [Text]
|
-> [Text]
|
||||||
-> HandlerT Auth (HandlerT site IO) TypedContent
|
-> AuthHandler site TypedContent
|
||||||
dispatch "GET" ["forward"] = do
|
dispatch "GET" ["forward"] = do
|
||||||
tm <- getRouteToParent
|
tm <- getRouteToParent
|
||||||
lift (getDest tm) >>= redirect
|
getDest tm >>= redirect
|
||||||
|
|
||||||
dispatch "GET" ["complete"] = do
|
dispatch "GET" ["complete"] = do
|
||||||
mstate <- lookupGetParam "state"
|
mstate <- lookupGetParam "state"
|
||||||
@ -207,30 +209,27 @@ authPlugin storeToken clientID clientSecret =
|
|||||||
case merr of
|
case merr of
|
||||||
Nothing -> invalidArgs ["Missing code paramter"]
|
Nothing -> invalidArgs ["Missing code paramter"]
|
||||||
Just err -> do
|
Just err -> do
|
||||||
master <- lift getYesod
|
master <- getYesod
|
||||||
let msg =
|
let msg =
|
||||||
case err of
|
case err of
|
||||||
"access_denied" -> "Access denied"
|
"access_denied" -> "Access denied"
|
||||||
_ -> "Unknown error occurred: " `T.append` err
|
_ -> "Unknown error occurred: " `T.append` err
|
||||||
addMessage "error" $ toHtml msg
|
addMessage "error" $ toHtml msg
|
||||||
lift $ redirect $ logoutDest master
|
redirect $ logoutDest master
|
||||||
Just c -> return c
|
Just c -> return c
|
||||||
|
|
||||||
render <- getUrlRender
|
render <- getUrlRender
|
||||||
|
tm <- getRouteToParent
|
||||||
|
|
||||||
req' <- liftIO $
|
req' <- liftIO $
|
||||||
#if MIN_VERSION_http_client(0,4,30)
|
|
||||||
HTTP.parseUrlThrow
|
HTTP.parseUrlThrow
|
||||||
#else
|
|
||||||
HTTP.parseUrl
|
|
||||||
#endif
|
|
||||||
"https://accounts.google.com/o/oauth2/token" -- FIXME don't hardcode, use: https://accounts.google.com/.well-known/openid-configuration
|
"https://accounts.google.com/o/oauth2/token" -- FIXME don't hardcode, use: https://accounts.google.com/.well-known/openid-configuration
|
||||||
let req =
|
let req =
|
||||||
urlEncodedBody
|
urlEncodedBody
|
||||||
[ ("code", encodeUtf8 code)
|
[ ("code", encodeUtf8 code)
|
||||||
, ("client_id", encodeUtf8 clientID)
|
, ("client_id", encodeUtf8 clientID)
|
||||||
, ("client_secret", encodeUtf8 clientSecret)
|
, ("client_secret", encodeUtf8 clientSecret)
|
||||||
, ("redirect_uri", encodeUtf8 $ render complete)
|
, ("redirect_uri", encodeUtf8 $ render $ tm complete)
|
||||||
, ("grant_type", "authorization_code")
|
, ("grant_type", "authorization_code")
|
||||||
]
|
]
|
||||||
req'
|
req'
|
||||||
@ -257,15 +256,12 @@ authPlugin storeToken clientID clientSecret =
|
|||||||
[e] -> return e
|
[e] -> return e
|
||||||
[] -> error "No account email"
|
[] -> error "No account email"
|
||||||
x -> error $ "Too many account emails: " ++ show x
|
x -> error $ "Too many account emails: " ++ show x
|
||||||
lift $ setCredsRedirect $ Creds pid email $ allPersonInfo personValue
|
setCredsRedirect $ Creds pid email $ allPersonInfo personValue
|
||||||
|
|
||||||
dispatch _ _ = notFound
|
dispatch _ _ = notFound
|
||||||
|
|
||||||
makeHttpRequest
|
makeHttpRequest :: Request -> AuthHandler site A.Value
|
||||||
:: (YesodAuth site)
|
makeHttpRequest req =
|
||||||
=> Request
|
|
||||||
-> HandlerT Auth (HandlerT site IO) A.Value
|
|
||||||
makeHttpRequest req = lift $
|
|
||||||
runHttpRequest req $ \res -> bodyReaderSource (responseBody res) $$ sinkParser json'
|
runHttpRequest req $ \res -> bodyReaderSource (responseBody res) $$ sinkParser json'
|
||||||
|
|
||||||
-- | Allows to fetch information about a user from Google's API.
|
-- | Allows to fetch information about a user from Google's API.
|
||||||
@ -273,7 +269,7 @@ makeHttpRequest req = lift $
|
|||||||
-- Will throw 'HttpException' in case of network problems or error response code.
|
-- Will throw 'HttpException' in case of network problems or error response code.
|
||||||
--
|
--
|
||||||
-- @since 1.4.3
|
-- @since 1.4.3
|
||||||
getPerson :: Manager -> Token -> HandlerT site IO (Maybe Person)
|
getPerson :: Manager -> Token -> AuthHandler site (Maybe Person)
|
||||||
getPerson manager token = parseMaybe parseJSON <$> (do
|
getPerson manager token = parseMaybe parseJSON <$> (do
|
||||||
req <- personValueRequest token
|
req <- personValueRequest token
|
||||||
res <- http req manager
|
res <- http req manager
|
||||||
@ -282,13 +278,8 @@ getPerson manager token = parseMaybe parseJSON <$> (do
|
|||||||
|
|
||||||
personValueRequest :: MonadIO m => Token -> m Request
|
personValueRequest :: MonadIO m => Token -> m Request
|
||||||
personValueRequest token = do
|
personValueRequest token = do
|
||||||
req2' <- liftIO $
|
req2' <- liftIO
|
||||||
#if MIN_VERSION_http_client(0,4,30)
|
$ HTTP.parseUrlThrow "https://www.googleapis.com/plus/v1/people/me"
|
||||||
HTTP.parseUrlThrow
|
|
||||||
#else
|
|
||||||
HTTP.parseUrl
|
|
||||||
#endif
|
|
||||||
"https://www.googleapis.com/plus/v1/people/me"
|
|
||||||
return req2'
|
return req2'
|
||||||
{ requestHeaders =
|
{ requestHeaders =
|
||||||
[ ("Authorization", encodeUtf8 $ "Bearer " `mappend` accessToken token)
|
[ ("Authorization", encodeUtf8 $ "Bearer " `mappend` accessToken token)
|
||||||
|
|||||||
@ -131,9 +131,10 @@ module Yesod.Auth.Hardcoded
|
|||||||
, loginR )
|
, loginR )
|
||||||
where
|
where
|
||||||
|
|
||||||
import Yesod.Auth (Auth, AuthPlugin (..), AuthRoute,
|
import Yesod.Auth (AuthPlugin (..), AuthRoute,
|
||||||
Creds (..), Route (..), YesodAuth,
|
Creds (..), Route (..), YesodAuth,
|
||||||
loginErrorMessageI, setCredsRedirect)
|
loginErrorMessageI, setCredsRedirect,
|
||||||
|
AuthHandler)
|
||||||
import qualified Yesod.Auth.Message as Msg
|
import qualified Yesod.Auth.Message as Msg
|
||||||
import Yesod.Core
|
import Yesod.Core
|
||||||
import Yesod.Form (ireq, runInputPost, textField)
|
import Yesod.Form (ireq, runInputPost, textField)
|
||||||
@ -148,10 +149,10 @@ loginR = PluginR "hardcoded" ["login"]
|
|||||||
class (YesodAuth site) => YesodAuthHardcoded site where
|
class (YesodAuth site) => YesodAuthHardcoded site where
|
||||||
|
|
||||||
-- | Check whether given user name exists among hardcoded names.
|
-- | Check whether given user name exists among hardcoded names.
|
||||||
doesUserNameExist :: Text -> HandlerT site IO Bool
|
doesUserNameExist :: Text -> AuthHandler site Bool
|
||||||
|
|
||||||
-- | Validate given user name with given password.
|
-- | Validate given user name with given password.
|
||||||
validatePassword :: Text -> Text -> HandlerT site IO Bool
|
validatePassword :: Text -> Text -> AuthHandler site Bool
|
||||||
|
|
||||||
|
|
||||||
authHardcoded :: YesodAuthHardcoded m => AuthPlugin m
|
authHardcoded :: YesodAuthHardcoded m => AuthPlugin m
|
||||||
@ -182,16 +183,16 @@ authHardcoded =
|
|||||||
|]
|
|]
|
||||||
|
|
||||||
|
|
||||||
postLoginR :: (YesodAuthHardcoded master)
|
postLoginR :: YesodAuthHardcoded site
|
||||||
=> HandlerT Auth (HandlerT master IO) TypedContent
|
=> AuthHandler site TypedContent
|
||||||
postLoginR =
|
postLoginR =
|
||||||
do (username, password) <- lift (runInputPost
|
do (username, password) <- runInputPost
|
||||||
((,) Control.Applicative.<$> ireq textField "username"
|
((,) Control.Applicative.<$> ireq textField "username"
|
||||||
Control.Applicative.<*> ireq textField "password"))
|
Control.Applicative.<*> ireq textField "password")
|
||||||
isValid <- lift (validatePassword username password)
|
isValid <- validatePassword username password
|
||||||
if isValid
|
if isValid
|
||||||
then lift (setCredsRedirect (Creds "hardcoded" username []))
|
then setCredsRedirect (Creds "hardcoded" username [])
|
||||||
else do isExists <- lift (doesUserNameExist username)
|
else do isExists <- doesUserNameExist username
|
||||||
loginErrorMessageI LoginR
|
loginErrorMessageI LoginR
|
||||||
(if isExists
|
(if isExists
|
||||||
then Msg.InvalidUsernamePass
|
then Msg.InvalidUsernamePass
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE RankNTypes #-}
|
{-# LANGUAGE RankNTypes #-}
|
||||||
|
{-# LANGUAGE GADTs #-}
|
||||||
module Yesod.Auth.OpenId
|
module Yesod.Auth.OpenId
|
||||||
( authOpenId
|
( authOpenId
|
||||||
, forwardUrl
|
, forwardUrl
|
||||||
@ -36,7 +37,10 @@ authOpenId idType extensionFields =
|
|||||||
AuthPlugin "openid" dispatch login
|
AuthPlugin "openid" dispatch login
|
||||||
where
|
where
|
||||||
complete = PluginR "openid" ["complete"]
|
complete = PluginR "openid" ["complete"]
|
||||||
|
|
||||||
|
name :: Text
|
||||||
name = "openid_identifier"
|
name = "openid_identifier"
|
||||||
|
|
||||||
login tm = do
|
login tm = do
|
||||||
ident <- newIdent
|
ident <- newIdent
|
||||||
-- FIXME this is a hack to get GHC 7.6's type checker to allow the
|
-- FIXME this is a hack to get GHC 7.6's type checker to allow the
|
||||||
@ -57,18 +61,20 @@ $newline never
|
|||||||
<input id="#{ident}" type="text" name="#{name}" value="http://">
|
<input id="#{ident}" type="text" name="#{name}" value="http://">
|
||||||
<input type="submit" value="_{Msg.LoginOpenID}">
|
<input type="submit" value="_{Msg.LoginOpenID}">
|
||||||
|]
|
|]
|
||||||
|
|
||||||
|
dispatch :: Text -> [Text] -> AuthHandler master TypedContent
|
||||||
dispatch "GET" ["forward"] = do
|
dispatch "GET" ["forward"] = do
|
||||||
roid <- lift $ runInputGet $ iopt textField name
|
roid <- runInputGet $ iopt textField name
|
||||||
case roid of
|
case roid of
|
||||||
Just oid -> do
|
Just oid -> do
|
||||||
|
tm <- getRouteToParent
|
||||||
render <- getUrlRender
|
render <- getUrlRender
|
||||||
let complete' = render complete
|
let complete' = render $ tm complete
|
||||||
master <- lift getYesod
|
manager <- authHttpManager
|
||||||
eres <- lift $ try $ OpenId.getForwardUrl oid complete' Nothing extensionFields (authHttpManager master)
|
eres <- liftResourceT $ try $ OpenId.getForwardUrl oid complete' Nothing extensionFields manager
|
||||||
case eres of
|
case eres of
|
||||||
Left err -> do
|
Left err -> do
|
||||||
tm <- getRouteToParent
|
loginErrorMessage (tm LoginR) $ T.pack $
|
||||||
lift $ loginErrorMessage (tm LoginR) $ T.pack $
|
|
||||||
show (err :: SomeException)
|
show (err :: SomeException)
|
||||||
Right x -> redirect x
|
Right x -> redirect x
|
||||||
Nothing -> loginErrorMessageI LoginR Msg.NoOpenID
|
Nothing -> loginErrorMessageI LoginR Msg.NoOpenID
|
||||||
@ -84,13 +90,13 @@ $newline never
|
|||||||
|
|
||||||
completeHelper :: IdentifierType -> [(Text, Text)] -> AuthHandler master TypedContent
|
completeHelper :: IdentifierType -> [(Text, Text)] -> AuthHandler master TypedContent
|
||||||
completeHelper idType gets' = do
|
completeHelper idType gets' = do
|
||||||
master <- lift getYesod
|
manager <- authHttpManager
|
||||||
eres <- try $ OpenId.authenticateClaimed gets' (authHttpManager master)
|
eres <- liftResourceT $ try $ OpenId.authenticateClaimed gets' manager
|
||||||
either onFailure onSuccess eres
|
either onFailure onSuccess eres
|
||||||
where
|
where
|
||||||
onFailure err = do
|
onFailure err = do
|
||||||
tm <- getRouteToParent
|
tm <- getRouteToParent
|
||||||
lift $ loginErrorMessage (tm LoginR) $ T.pack $
|
loginErrorMessage (tm LoginR) $ T.pack $
|
||||||
show (err :: SomeException)
|
show (err :: SomeException)
|
||||||
onSuccess oir = do
|
onSuccess oir = do
|
||||||
let claimed =
|
let claimed =
|
||||||
@ -105,7 +111,7 @@ completeHelper idType gets' = do
|
|||||||
case idType of
|
case idType of
|
||||||
OPLocal -> OpenId.oirOpLocal oir
|
OPLocal -> OpenId.oirOpLocal oir
|
||||||
Claimed -> fromMaybe (OpenId.oirOpLocal oir) $ OpenId.oirClaimed oir
|
Claimed -> fromMaybe (OpenId.oirOpLocal oir) $ OpenId.oirClaimed oir
|
||||||
lift $ setCredsRedirect $ Creds "openid" i gets''
|
setCredsRedirect $ Creds "openid" i gets''
|
||||||
|
|
||||||
-- | The main identifier provided by the OpenID authentication plugin is the
|
-- | The main identifier provided by the OpenID authentication plugin is the
|
||||||
-- \"OP-local identifier\". There is also sometimes a \"claimed\" identifier
|
-- \"OP-local identifier\". There is also sometimes a \"claimed\" identifier
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE RankNTypes #-}
|
{-# LANGUAGE RankNTypes #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE GADTs #-}
|
||||||
module Yesod.Auth.Rpxnow
|
module Yesod.Auth.Rpxnow
|
||||||
( authRpxnow
|
( authRpxnow
|
||||||
) where
|
) where
|
||||||
@ -17,10 +18,10 @@ import Data.Text.Encoding.Error (lenientDecode)
|
|||||||
import Control.Arrow ((***))
|
import Control.Arrow ((***))
|
||||||
import Network.HTTP.Types (renderQuery)
|
import Network.HTTP.Types (renderQuery)
|
||||||
|
|
||||||
authRpxnow :: YesodAuth m
|
authRpxnow :: YesodAuth master
|
||||||
=> String -- ^ app name
|
=> String -- ^ app name
|
||||||
-> String -- ^ key
|
-> String -- ^ key
|
||||||
-> AuthPlugin m
|
-> AuthPlugin master
|
||||||
authRpxnow app apiKey =
|
authRpxnow app apiKey =
|
||||||
AuthPlugin "rpxnow" dispatch login
|
AuthPlugin "rpxnow" dispatch login
|
||||||
where
|
where
|
||||||
@ -32,14 +33,17 @@ authRpxnow app apiKey =
|
|||||||
$newline never
|
$newline never
|
||||||
<iframe src="http://#{app}.rpxnow.com/openid/embed#{queryString}" scrolling="no" frameBorder="no" allowtransparency="true" style="width:400px;height:240px">
|
<iframe src="http://#{app}.rpxnow.com/openid/embed#{queryString}" scrolling="no" frameBorder="no" allowtransparency="true" style="width:400px;height:240px">
|
||||||
|]
|
|]
|
||||||
|
|
||||||
|
dispatch :: a -> [b] -> AuthHandler master TypedContent
|
||||||
dispatch _ [] = do
|
dispatch _ [] = do
|
||||||
token1 <- lookupGetParams "token"
|
token1 <- lookupGetParams "token"
|
||||||
token2 <- lookupPostParams "token"
|
token2 <- lookupPostParams "token"
|
||||||
token <- case token1 ++ token2 of
|
token <- case token1 ++ token2 of
|
||||||
[] -> invalidArgs ["token: Value not supplied"]
|
[] -> invalidArgs ["token: Value not supplied"]
|
||||||
x:_ -> return $ unpack x
|
x:_ -> return $ unpack x
|
||||||
master <- lift getYesod
|
manager <- authHttpManager
|
||||||
Rpxnow.Identifier ident extra <- lift $ Rpxnow.authenticate apiKey token (authHttpManager master)
|
Rpxnow.Identifier ident extra <-
|
||||||
|
liftResourceT $ Rpxnow.authenticate apiKey token manager
|
||||||
let creds =
|
let creds =
|
||||||
Creds "rpxnow" ident
|
Creds "rpxnow" ident
|
||||||
$ maybe id (\x -> (:) ("verifiedEmail", x))
|
$ maybe id (\x -> (:) ("verifiedEmail", x))
|
||||||
@ -47,7 +51,7 @@ $newline never
|
|||||||
$ maybe id (\x -> (:) ("displayName", x))
|
$ maybe id (\x -> (:) ("displayName", x))
|
||||||
(fmap pack $ getDisplayName $ map (unpack *** unpack) extra)
|
(fmap pack $ getDisplayName $ map (unpack *** unpack) extra)
|
||||||
[]
|
[]
|
||||||
lift $ setCredsRedirect creds
|
setCredsRedirect creds
|
||||||
dispatch _ _ = notFound
|
dispatch _ _ = notFound
|
||||||
|
|
||||||
-- | Get some form of a display name.
|
-- | Get some form of a display name.
|
||||||
|
|||||||
@ -38,9 +38,9 @@ library
|
|||||||
, unordered-containers
|
, unordered-containers
|
||||||
, yesod-form >= 1.4 && < 1.5
|
, yesod-form >= 1.4 && < 1.5
|
||||||
, transformers >= 0.2.2
|
, transformers >= 0.2.2
|
||||||
, persistent >= 2.1 && < 2.8
|
, persistent >= 2.5 && < 2.8
|
||||||
, persistent-template >= 2.1 && < 2.8
|
, persistent-template >= 2.1 && < 2.8
|
||||||
, http-client
|
, http-client >= 0.5
|
||||||
, http-client-tls
|
, http-client-tls
|
||||||
, http-conduit >= 2.1
|
, http-conduit >= 2.1
|
||||||
, aeson >= 0.7
|
, aeson >= 0.7
|
||||||
@ -76,7 +76,6 @@ library
|
|||||||
Yesod.Auth.OpenId
|
Yesod.Auth.OpenId
|
||||||
Yesod.Auth.Rpxnow
|
Yesod.Auth.Rpxnow
|
||||||
Yesod.Auth.Message
|
Yesod.Auth.Message
|
||||||
Yesod.Auth.GoogleEmail
|
|
||||||
Yesod.Auth.GoogleEmail2
|
Yesod.Auth.GoogleEmail2
|
||||||
Yesod.Auth.Hardcoded
|
Yesod.Auth.Hardcoded
|
||||||
Yesod.Auth.Util.PasswordStore
|
Yesod.Auth.Util.PasswordStore
|
||||||
|
|||||||
@ -78,7 +78,6 @@ import Language.Haskell.TH.Syntax as TH
|
|||||||
|
|
||||||
import Crypto.Hash.Conduit (hashFile, sinkHash)
|
import Crypto.Hash.Conduit (hashFile, sinkHash)
|
||||||
import Crypto.Hash (MD5, Digest)
|
import Crypto.Hash (MD5, Digest)
|
||||||
import Control.Monad.Catch (MonadThrow)
|
|
||||||
import Control.Monad.Trans.State
|
import Control.Monad.Trans.State
|
||||||
|
|
||||||
import qualified Data.ByteArray as ByteArray
|
import qualified Data.ByteArray as ByteArray
|
||||||
@ -175,12 +174,10 @@ instance RenderRoute Static where
|
|||||||
instance ParseRoute Static where
|
instance ParseRoute Static where
|
||||||
parseRoute (x, y) = Just $ StaticRoute x y
|
parseRoute (x, y) = Just $ StaticRoute x y
|
||||||
|
|
||||||
instance (MonadThrow m, MonadIO m, MonadBaseControl IO m)
|
instance MonadHandler m => YesodSubDispatch Static m where
|
||||||
=> YesodSubDispatch Static (HandlerT master m) where
|
|
||||||
yesodSubDispatch YesodSubRunnerEnv {..} req =
|
yesodSubDispatch YesodSubRunnerEnv {..} req =
|
||||||
ysreParentRunner base ysreParentEnv (fmap ysreToParentRoute route) req
|
ysreParentRunner handlert ysreParentEnv (fmap ysreToParentRoute route) req
|
||||||
where
|
where
|
||||||
base = stripHandlerT handlert ysreGetSub ysreToParentRoute route
|
|
||||||
route = Just $ StaticRoute (pathInfo req) []
|
route = Just $ StaticRoute (pathInfo req) []
|
||||||
|
|
||||||
Static set = ysreGetSub $ yreSite $ ysreParentEnv
|
Static set = ysreGetSub $ yreSite $ ysreParentEnv
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user