-- Hoogle documentation, generated by Haddock
-- See Hoogle, http://www.haskell.org/hoogle/


-- | An account authentication plugin for Yesod
--   
--   An auth plugin for accounts. Each account consists of a username,
--   email, and password. The plugin provides new account, email
--   verification, and password reset pages that can be customized to
--   enhance the user experience.
@package yesod-auth-account
@version 1.2.2


-- | An auth plugin for accounts. Each account consists of a username,
--   email, and password.
--   
--   This module is designed so that you can use the default pages for
--   login, account creation, change password, etc. But the module also
--   exports some forms which you can embed into your own pages,
--   customizing the account process. The minimal requirements to use this
--   module are:
--   
--   <ul>
--   <li>If you are not using persistent or just want more control over the
--   user data, you can use any datatype for user information and make it
--   an instance of <a>UserCredentials</a>. You must also create an
--   instance of <a>AccountDB</a>.</li>
--   <li>You may use a user datatype created by persistent, in which case
--   you can make the datatype an instance of <a>PersistUserCredentials</a>
--   instead of <a>UserCredentials</a>. In this case,
--   <a>AccountPersistDB</a> from this module already implements the
--   <a>AccountDB</a> interface for you.</li>
--   <li>Make your master site an instance of <a>AccountSendEmail</a>. By
--   default, this class just logs a message so during development this
--   class requires no implementation.</li>
--   <li>Make your master site and database an instance of
--   <a>YesodAuthAccount</a>. There is only one required function which
--   must be implemented (<a>runAccountDB</a>) although there are several
--   functions you can override in this class to customize the behavior of
--   this module.</li>
--   <li>Include <a>accountPlugin</a> in the list of plugins in your
--   instance of <a>YesodAuth</a>.</li>
--   </ul>
module Yesod.Auth.Account

-- | Each user is uniquely identified by a username.
type Username = Text

-- | Route for the default new account page.
--   
--   See the New Account section below for customizing the new account
--   process.
newAccountR :: AuthRoute

-- | Route for the reset password page.
--   
--   This page allows the user to reset their password by requesting an
--   email with a reset URL be sent to them. See the Password Reset section
--   below for customization.
resetPasswordR :: AuthRoute

-- | The account authentication plugin. Here is a complete example using
--   persistent.
--   
--   <pre>
--   {-# LANGUAGE QuasiQuotes, TypeFamilies, GeneralizedNewtypeDeriving #-}
--   {-# LANGUAGE FlexibleContexts, FlexibleInstances, TemplateHaskell, OverloadedStrings #-}
--   {-# LANGUAGE GADTs, MultiParamTypeClasses, TypeSynonymInstances #-}
--   
--   import Data.Text (Text)
--   import Data.ByteString (ByteString)
--   import Database.Persist.Sqlite
--   import Control.Monad.Logger (runStderrLoggingT)
--   import Yesod
--   import Yesod.Auth
--   import Yesod.Auth.Account
--   
--   share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistUpperCase|
--   User
--       username Text
--       UniqueUsername username
--       password ByteString
--       emailAddress Text
--       verified Bool
--       verifyKey Text
--       resetPasswordKey Text
--       deriving Show
--   |]
--   
--   instance PersistUserCredentials User where
--       userUsernameF = UserUsername
--       userPasswordHashF = UserPassword
--       userEmailF = UserEmailAddress
--       userEmailVerifiedF = UserVerified
--       userEmailVerifyKeyF = UserVerifyKey
--       userResetPwdKeyF = UserResetPasswordKey
--       uniqueUsername = UniqueUsername
--   
--       userCreate name email key pwd = User name pwd email False key ""
--   
--   data MyApp = MyApp ConnectionPool
--   
--   mkYesod "MyApp" [parseRoutes|
--   / HomeR GET
--   /auth AuthR Auth getAuth
--   |]
--   
--   instance Yesod MyApp
--   
--   instance RenderMessage MyApp FormMessage where
--       renderMessage _ _ = defaultFormMessage
--   
--   instance YesodPersist MyApp where
--       type YesodPersistBackend MyApp = SqlPersistT
--       runDB action = do
--           MyApp pool &lt;- getYesod
--           runSqlPool action pool
--   
--   instance YesodAuth MyApp where
--       type AuthId MyApp = Username
--       getAuthId = return . Just . credsIdent
--       loginDest _ = HomeR
--       logoutDest _ = HomeR
--       authPlugins _ = [accountPlugin]
--       authHttpManager _ = error "No manager needed"
--       onLogin = return ()
--       maybeAuthId = lookupSession "_ID"
--   
--   instance AccountSendEmail MyApp
--   
--   instance YesodAuthAccount (AccountPersistDB MyApp User) MyApp where
--       runAccountDB = runAccountPersistDB
--   
--   getHomeR :: Handler Html
--   getHomeR = do
--       maid &lt;- maybeAuthId
--       case maid of
--           Nothing -&gt; defaultLayout $ [whamlet|
--   &lt;p&gt;Please visit the &lt;a href="@{AuthR LoginR}"&gt;Login page&lt;/a&gt;
--   |]
--           Just u -&gt; defaultLayout $ [whamlet|
--   &lt;p&gt;You are logged in as #{u}
--   &lt;p&gt;&lt;a href="@{AuthR LogoutR}"&gt;Logout&lt;/a&gt;
--   |]
--   
--   main :: IO ()
--   main = withSqlitePool "test.db3" 10 $ \pool -&gt; do
--       runStderrLoggingT $ runSqlPool (runMigration migrateAll) pool
--       warp 3000 $ MyApp pool
--   </pre>
accountPlugin :: YesodAuthAccount db master => AuthPlugin master

-- | The data collected in the login form.
data LoginData
LoginData :: Text -> Text -> LoginData
loginUsername :: LoginData -> Text
loginPassword :: LoginData -> Text

-- | The login form.
--   
--   You can embed this form into your own pages if you want a custom
--   rendering of this form or to include a login form on your own pages.
--   The form submission should be posted to <a>loginFormPostTargetR</a>.
loginForm :: (MonadHandler m, YesodAuthAccount db master, HandlerSite m ~ master) => AForm m LoginData

-- | The POST target for the <a>loginForm</a>.
loginFormPostTargetR :: AuthRoute

-- | A default rendering of <a>loginForm</a> using renderDivs.
--   
--   This is the widget used in the default implementation of
--   <a>loginHandler</a>. The widget also includes links to the new account
--   and reset password pages.
loginWidget :: YesodAuthAccount db master => (Route Auth -> Route master) -> WidgetT master IO ()

-- | The URL sent in an email for email verification
verifyR :: Username -> Text -> AuthRoute

-- | The data collected in the new account form.
data NewAccountData
NewAccountData :: Username -> Text -> Text -> Text -> NewAccountData
newAccountUsername :: NewAccountData -> Username
newAccountEmail :: NewAccountData -> Text
newAccountPassword1 :: NewAccountData -> Text
newAccountPassword2 :: NewAccountData -> Text

-- | The new account form.
--   
--   You can embed this form into your own pages or into
--   <a>getNewAccountR</a>. The form submission should be posted to
--   <a>newAccountR</a>. Alternatively, you could embed this form into a
--   larger form where you prompt for more information during account
--   creation. In this case, the NewAccountData should be passed to
--   <a>createNewAccount</a> from inside <a>postNewAccountR</a>.
newAccountForm :: (YesodAuth master, RenderMessage master FormMessage, MonadHandler m, HandlerSite m ~ master) => AForm m NewAccountData

-- | A default rendering of the <a>newAccountForm</a> using renderDivs.
newAccountWidget :: (YesodAuth master, RenderMessage master FormMessage) => (Route Auth -> Route master) -> WidgetT master IO ()

-- | An action to create a new account.
--   
--   You can use this action inside your own implementation of
--   <a>postNewAccountR</a> if you add additional fields to the new account
--   creation. This action assumes the user has not yet been created in the
--   database and will create the user, so this action should be run first
--   in your handler. Note that this action does not check if the passwords
--   are equal. If an error occurs (username exists, etc.) this will set a
--   message and redirect to <a>newAccountR</a>.
createNewAccount :: YesodAuthAccount db master => NewAccountData -> (Route Auth -> Route master) -> HandlerT master IO (UserAccount db)

-- | A form to allow the user to request the email validation be resent.
--   
--   Intended for use in <a>unregisteredLogin</a>. The result should be
--   posted to <a>resendVerifyR</a>.
resendVerifyEmailForm :: (RenderMessage master FormMessage, MonadHandler m, HandlerSite m ~ master) => Username -> AForm m Username

-- | The POST target for resending a verification email
resendVerifyR :: AuthRoute

-- | A default rendering of <a>resendVerifyEmailForm</a>
resendVerifyEmailWidget :: RenderMessage master FormMessage => Username -> (Route Auth -> Route master) -> WidgetT master IO ()

-- | The URL sent in an email when the user requests to reset their
--   password
newPasswordR :: Username -> Text -> AuthRoute

-- | A form for the user to request that an email be sent to them to allow
--   them to reset their password. This form contains a field for the
--   username (plus the CSRF token). The form should be posted to
--   <a>resetPasswordR</a>.
resetPasswordForm :: (RenderMessage master FormMessage, MonadHandler m, HandlerSite m ~ master) => AForm m Username

-- | A default rendering of <a>resetPasswordForm</a>.
resetPasswordWidget :: (YesodAuth master, RenderMessage master FormMessage) => (Route Auth -> Route master) -> WidgetT master IO ()

-- | The data for setting a new password.
data NewPasswordData
NewPasswordData :: Username -> Text -> Text -> Text -> NewPasswordData
newPasswordUser :: NewPasswordData -> Username
newPasswordKey :: NewPasswordData -> Text
newPasswordPwd1 :: NewPasswordData -> Text
newPasswordPwd2 :: NewPasswordData -> Text

-- | The form for setting a new password. It contains hidden fields for the
--   username and key and prompts for the passwords. This form should be
--   posted to <a>setPasswordR</a>.
newPasswordForm :: (YesodAuth master, RenderMessage master FormMessage, MonadHandler m, HandlerSite m ~ master) => Username -> Text -> AForm m NewPasswordData

-- | The POST target for reseting the password
setPasswordR :: AuthRoute

-- | A default rendering of <a>newPasswordForm</a>.
newPasswordWidget :: YesodAuthAccount db master => UserAccount db -> (Route Auth -> Route master) -> WidgetT master IO ()

-- | Interface for the data type which stores the user info when not using
--   persistent.
--   
--   You must make a data type that is either an instance of this class or
--   of <a>PersistUserCredentials</a>, depending on if you are using
--   persistent or not.
--   
--   Users are uniquely identified by their username, and for each user we
--   must store the email, the verify status, a hashed user password, and a
--   reset password key. The format for the hashed password is the format
--   from <a>Crypto.PasswordStore</a>. If the email has been verified and
--   no password reset is in progress, the relevent keys should be the
--   empty string.
class UserCredentials u
username :: UserCredentials u => u -> Username
userPasswordHash :: UserCredentials u => u -> ByteString
userEmail :: UserCredentials u => u -> Text
userEmailVerified :: UserCredentials u => u -> Bool
userEmailVerifyKey :: UserCredentials u => u -> Text
userResetPwdKey :: UserCredentials u => u -> Text

-- | Interface for the data type which stores the user info when using
--   persistent.
--   
--   You must make a data type that is either an instance of this class or
--   of <a>UserCredentials</a>, depending on if you are using persistent or
--   not.
class PersistUserCredentials u
userUsernameF :: PersistUserCredentials u => EntityField u Username
userPasswordHashF :: PersistUserCredentials u => EntityField u ByteString
userEmailF :: PersistUserCredentials u => EntityField u Text
userEmailVerifiedF :: PersistUserCredentials u => EntityField u Bool
userEmailVerifyKeyF :: PersistUserCredentials u => EntityField u Text
userResetPwdKeyF :: PersistUserCredentials u => EntityField u Text
uniqueUsername :: PersistUserCredentials u => Text -> Unique u
userCreate :: PersistUserCredentials u => Username -> Text -> Text -> ByteString -> u

-- | These are the database operations to load and update user data.
--   
--   Persistent users can use <a>AccountPersistDB</a> and don't need to
--   create their own instance. If you are not using persistent or are
--   using persistent but want to customize the database activity, you must
--   manually make a monad an instance of this class. You can use any monad
--   for which you can write <a>runAccountDB</a>, but typically the monad
--   will be a newtype of HandlerT. For example,
--   
--   <pre>
--   newtype MyAccountDB a = MyAccountDB {runMyAccountDB :: HandlerT MyApp IO a}
--      deriving (Monad, MonadIO)
--   instance AccountDB MyAccountDB where
--       ....
--   </pre>
class AccountDB m where type family UserAccount m
loadUser :: AccountDB m => Username -> m (Maybe (UserAccount m))
addNewUser :: AccountDB m => Username -> Text -> Text -> ByteString -> m (Either Text (UserAccount m))
verifyAccount :: AccountDB m => UserAccount m -> m ()
setVerifyKey :: AccountDB m => UserAccount m -> Text -> m ()
setNewPasswordKey :: AccountDB m => UserAccount m -> Text -> m ()
setNewPassword :: AccountDB m => UserAccount m -> ByteString -> m ()

-- | A class to send email.
--   
--   Both of the methods are implemented by default to just log a message,
--   so during development there are no required methods. For production, I
--   recommend <a>http://hackage.haskell.org/package/mime-mail</a>.
class AccountSendEmail master where sendVerifyEmail uname email url = $logInfo $ concat ["Verification email for ", uname, " (", email, "): ", url] sendNewPasswordEmail uname email url = $logInfo $ concat ["Reset password email for ", uname, " (", email, "): ", url]
sendVerifyEmail :: AccountSendEmail master => Username -> Text -> Text -> HandlerT master IO ()
sendNewPasswordEmail :: AccountSendEmail master => Username -> Text -> Text -> HandlerT master IO ()

-- | A newtype which when using persistent is an instance of
--   <a>AccountDB</a>.
data AccountPersistDB master user a

-- | Use this for <a>runAccountDB</a> if you are using
--   <a>AccountPersistDB</a> as your database type.
runAccountPersistDB :: (Yesod master, YesodPersist master, PersistEntity user, PersistUserCredentials user, b ~ YesodPersistBackend master, PersistMonadBackend (b (HandlerT master IO)) ~ PersistEntityBackend user, PersistUnique (b (HandlerT master IO)), PersistQuery (b (HandlerT master IO))) => AccountPersistDB master user a -> HandlerT master IO a

-- | The main class controlling the account plugin.
--   
--   You must make your database instance of <a>AccountDB</a> and your
--   master site an instance of this class. The only required method is
--   <a>runAccountDB</a>, although this class contains many other methods
--   to customize the behavior of the account plugin.
--   
--   Continuing the example from the manual creation of <a>AccountDB</a>, a
--   minimal instance is
--   
--   <pre>
--   instance YesodAuthAccount MyAccountDB MyApp where
--       runAccountDB = runMyAccountDB
--   </pre>
--   
--   If instead you are using persistent and have made an instance of
--   <a>PersistUserCredentials</a>, a minimal instance is
--   
--   <pre>
--   instance YesodAuthAccount (AccountPersistDB MyApp User) MyApp where
--      runAccountDB = runAccountPersistDB
--   </pre>
class (YesodAuth master, AccountSendEmail master, AccountDB db, UserCredentials (UserAccount db), RenderMessage master FormMessage) => YesodAuthAccount db master | master -> db where checkValidUsername u | all isAlphaNum u = return $ Right u checkValidUsername _ = do { mr <- getMessageRender; return $ Left $ mr MsgInvalidUsername } unregisteredLogin u = do { tm <- getRouteToParent; lift $ defaultLayout $ do { setTitleI MsgEmailUnverified; do { (asWidgetT . toWidget) ((preEscapedText . pack) "<p>"); ((liftM (toHtml .) getMessageRender) >>= (\ urender_a9Q4 -> (asWidgetT . toWidget) (urender_a9Q4 MsgEmailUnverified))); (asWidgetT . toWidget) ((preEscapedText . pack) "</p>"); (asWidgetT . toWidget) (resendVerifyEmailWidget (username u) tm) } } } getNewAccountR = do { tm <- getRouteToParent; lift $ defaultLayout $ do { setTitleI RegisterLong; newAccountWidget tm } } postNewAccountR = do { tm <- getRouteToParent; mr <- lift getMessageRender; ((result, _), _) <- lift $ runFormPost $ renderDivs newAccountForm; mdata <- case result of { FormMissing -> invalidArgs ["Form is missing"] FormFailure msg -> return $ Left msg FormSuccess d -> return $ if newAccountPassword1 d == newAccountPassword2 d then Right d else Left [mr PassMismatch] }; case mdata of { Left errs -> do { setMessage $ toHtml $ concat errs; redirect newAccountR } Right d -> do { void $ lift $ createNewAccount d tm; redirect LoginR } } } allowPasswordReset _ = True getResetPasswordR = do { tm <- getRouteToParent; lift $ defaultLayout $ do { setTitleI PasswordResetTitle; resetPasswordWidget tm } } setPasswordHandler u = do { tm <- getRouteToParent; lift $ defaultLayout $ do { setTitleI SetPassTitle; newPasswordWidget u tm } }
runAccountDB :: YesodAuthAccount db master => db a -> HandlerT master IO a
checkValidUsername :: (YesodAuthAccount db master, MonadHandler m, HandlerSite m ~ master) => Username -> m (Either Text Username)
unregisteredLogin :: YesodAuthAccount db master => UserAccount db -> HandlerT Auth (HandlerT master IO) Html
getNewAccountR :: YesodAuthAccount db master => HandlerT Auth (HandlerT master IO) Html
postNewAccountR :: YesodAuthAccount db master => HandlerT Auth (HandlerT master IO) Html
allowPasswordReset :: YesodAuthAccount db master => master -> Bool
getResetPasswordR :: YesodAuthAccount db master => HandlerT Auth (HandlerT master IO) Html
setPasswordHandler :: YesodAuthAccount db master => UserAccount db -> HandlerT Auth (HandlerT master IO) Html

-- | Salt and hash a password.
hashPassword :: MonadIO m => Text -> m ByteString

-- | Verify a password
verifyPassword :: Text -> ByteString -> Bool

-- | Randomly create a new verification key.
newVerifyKey :: MonadIO m => m Text
instance Show LoginData
instance Show NewAccountData
instance Show NewPasswordData
instance Monad (AccountPersistDB master user)
instance MonadIO (AccountPersistDB master user)
instance (Yesod master, PersistUserCredentials user) => AccountDB (AccountPersistDB master user)
instance (PersistEntity u, PersistUserCredentials u) => UserCredentials (Entity u)
instance RenderMessage m AccountMsg
