aboutsummaryrefslogtreecommitdiff
path: root/src/server/Controller/SignIn.hs
blob: a46894a9aeff4a3196583b4995b9d3b02fdc6618 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
{-# LANGUAGE OverloadedStrings #-}

module Controller.SignIn
  ( signInAction
  , validateSignInAction
  ) where

import Web.Scotty

import Network.HTTP.Types.Status (ok200, badRequest400)

import Database.Persist

import Control.Monad.IO.Class (liftIO)

import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Encoding as TE
import Data.Time.Clock (getCurrentTime, diffUTCTime)

import qualified LoginSession

import Config

import SendMail

import Text.Email.Validate (isValid)

import Model.Database
import Model.User
import Model.SignIn
import Model.Message

import qualified View.Mail.SignIn as SignIn

signInAction :: Config -> Text -> ActionM ()
signInAction config login =
  if isValid (TE.encodeUtf8 login)
    then do
      maybeUser <- liftIO . runDb $ getUser login
      case maybeUser of
        Just user -> do
          token <- liftIO . runDb $ createSignInToken login
          let url = T.concat ["http://", hostname config, "/validateSignIn?token=", token]
          maybeSentMail <- liftIO . sendMail $ SignIn.getMail (entityVal user) url [login]
          case maybeSentMail of
            Right _ ->
              status ok200
            Left _ ->
              errorResponse "Sorry, we failed to send you the sign up email."
        Nothing ->
            errorResponse "You are not authorized to sign in."
    else
      errorResponse "Please enter a valid email address."

errorResponse :: Text -> ActionM ()
errorResponse msg = do
  status badRequest400
  json (Message msg)

validateSignInAction :: Text -> ActionM ()
validateSignInAction token = do
  maybeSignIn <- liftIO . runDb $ getSignInToken token
  now <- liftIO getCurrentTime
  case maybeSignIn of
    Just signIn ->
      if signInIsUsed . entityVal $ signIn
        then
          redirectError "The token has already been used."
        else
          let diffTime = now `diffUTCTime` (signInCreation . entityVal $ signIn)
          in  if diffTime > 2 * 60 -- 2 minutes
                then
                  redirectError "The token has expired."
                else do
                  LoginSession.put (signInEmail . entityVal $ signIn)
                  liftIO . runDb . signInTokenToUsed . entityKey $ signIn
                  redirect "/"
    Nothing ->
      redirectError "The token is invalid."

redirectError :: Text -> ActionM ()
redirectError msg =
  redirect . TL.fromStrict . T.concat $ ["/?signInError=", msg]