-- 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:
--   
--   
--   
- 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 UserCredentials. You must also create an
--   instance of AccountDB.
--   
- You may use a user datatype created by persistent, in which case
--   you can make the datatype an instance of PersistUserCredentials
--   instead of UserCredentials. In this case,
--   AccountPersistDB from this module already implements the
--   AccountDB interface for you.
--   
- Make your master site an instance of AccountSendEmail. By
--   default, this class just logs a message so during development this
--   class requires no implementation.
--   
- Make your master site and database an instance of
--   YesodAuthAccount. There is only one required function which
--   must be implemented (runAccountDB) although there are several
--   functions you can override in this class to customize the behavior of
--   this module.
--   
- Include accountPlugin in the list of plugins in your
--   instance of YesodAuth.
--   
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.
--   
--   
--   {-# 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 <- 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 <- maybeAuthId
--       case maid of
--           Nothing -> defaultLayout $ [whamlet|
--   <p>Please visit the <a href="@{AuthR LoginR}">Login page</a>
--   |]
--           Just u -> defaultLayout $ [whamlet|
--   <p>You are logged in as #{u}
--   <p><a href="@{AuthR LogoutR}">Logout</a>
--   |]
--   
--   main :: IO ()
--   main = withSqlitePool "test.db3" 10 $ \pool -> do
--       runStderrLoggingT $ runSqlPool (runMigration migrateAll) pool
--       warp 3000 $ MyApp pool
--   
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 loginFormPostTargetR.
loginForm :: (MonadHandler m, YesodAuthAccount db master, HandlerSite m ~ master) => AForm m LoginData

-- | The POST target for the loginForm.
loginFormPostTargetR :: AuthRoute

-- | A default rendering of loginForm using renderDivs.
--   
--   This is the widget used in the default implementation of
--   loginHandler. 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
--   getNewAccountR. The form submission should be posted to
--   newAccountR. 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
--   createNewAccount from inside postNewAccountR.
newAccountForm :: (YesodAuth master, RenderMessage master FormMessage, MonadHandler m, HandlerSite m ~ master) => AForm m NewAccountData

-- | A default rendering of the newAccountForm 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
--   postNewAccountR 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 newAccountR.
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 unregisteredLogin. The result should be
--   posted to resendVerifyR.
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 resendVerifyEmailForm
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
--   resetPasswordR.
resetPasswordForm :: (RenderMessage master FormMessage, MonadHandler m, HandlerSite m ~ master) => AForm m Username

-- | A default rendering of resetPasswordForm.
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 setPasswordR.
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 newPasswordForm.
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 PersistUserCredentials, 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 Crypto.PasswordStore. 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 UserCredentials, 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 AccountPersistDB 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 runAccountDB, but typically the monad
--   will be a newtype of HandlerT. For example,
--   
--   
--   newtype MyAccountDB a = MyAccountDB {runMyAccountDB :: HandlerT MyApp IO a}
--      deriving (Monad, MonadIO)
--   instance AccountDB MyAccountDB where
--       ....
--   
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 http://hackage.haskell.org/package/mime-mail.
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
--   AccountDB.
data AccountPersistDB master user a

-- | Use this for runAccountDB if you are using
--   AccountPersistDB 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 AccountDB and your
--   master site an instance of this class. The only required method is
--   runAccountDB, although this class contains many other methods
--   to customize the behavior of the account plugin.
--   
--   Continuing the example from the manual creation of AccountDB, a
--   minimal instance is
--   
--   
--   instance YesodAuthAccount MyAccountDB MyApp where
--       runAccountDB = runMyAccountDB
--   
--   
--   If instead you are using persistent and have made an instance of
--   PersistUserCredentials, a minimal instance is
--   
--   
--   instance YesodAuthAccount (AccountPersistDB MyApp User) MyApp where
--      runAccountDB = runAccountPersistDB
--   
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) "
"); ((liftM (toHtml .) getMessageRender) >>= (\ urender_a9Q4 -> (asWidgetT . toWidget) (urender_a9Q4 MsgEmailUnverified))); (asWidgetT . toWidget) ((preEscapedText . pack) "
"); (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
