about summary refs log tree commit diff
path: root/src/App.hs
diff options
context:
space:
mode:
authorWilliam Carroll <wpcarro@gmail.com>2020-07-28T20·33+0100
committerWilliam Carroll <wpcarro@gmail.com>2020-07-28T20·33+0100
commitcf6c8799ab86278c827d4236a7a89163c61c29b9 (patch)
treefe34a5b41f63a44ff961af2a85a27e625220291d /src/App.hs
parentf051b0be0bc360c949b3b1913f13c4856ae317ca (diff)
Restrict users from multiple failed login attempts
I'm not resetting the failed LoginAttempt count, which is a low priority for
now, but necessary eventually.
Diffstat (limited to 'src/App.hs')
-rw-r--r--src/App.hs33
1 files changed, 25 insertions, 8 deletions
diff --git a/src/App.hs b/src/App.hs
index 786820f097a3..209e2f209231 100644
--- a/src/App.hs
+++ b/src/App.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE TypeApplications #-}
 --------------------------------------------------------------------------------
 module App where
@@ -20,6 +21,7 @@ import qualified Types as T
 import qualified Accounts as Accounts
 import qualified Trips as Trips
 import qualified Sessions as Sessions
+import qualified LoginAttempts as LoginAttempts
 --------------------------------------------------------------------------------
 
 server :: FilePath -> Server API
@@ -76,14 +78,29 @@ server dbFile = createAccountH
     login (T.AccountCredentials username password) = do
       mAccount <- Accounts.lookup dbFile username
       case mAccount of
-        Just account ->
-          if T.passwordsMatch password (T.accountPassword account) then do
-            session <- Sessions.findOrCreate dbFile account
-            -- set cookie
-            pure NoContent
-          else
-            -- TODO(wpcarro): Catch and return errors over HTTP
-            throwIO $ err401 { errBody = "Your credentials are invalid" }
+        Just account@T.Account{..} -> do
+          mAttempts <- LoginAttempts.forUsername dbFile accountUsername
+          case mAttempts of
+            Nothing ->
+              if T.passwordsMatch password accountPassword then do
+                session <- Sessions.findOrCreate dbFile account
+                -- set cookie
+                pure NoContent
+              else do
+                LoginAttempts.increment dbFile username
+                throwIO $ err401 { errBody = "Your credentials are invalid" }
+            Just attempts ->
+              if attempts > 3 then
+                -- TODO(wpcarro): Prefer 429 error code
+                throwIO $ err401 { errBody = "Too many failed login attempts" }
+              else if T.passwordsMatch password accountPassword then do
+                session <- Sessions.findOrCreate dbFile account
+                -- set cookie
+                pure NoContent
+              else do
+                LoginAttempts.increment dbFile username
+                -- TODO(wpcarro): Catch and return errors over HTTP
+                throwIO $ err401 { errBody = "Your credentials are invalid" }
 
         -- In this branch, the user didn't supply a known username.
         Nothing -> throwIO $ err401 { errBody = "Your credentials are invalid" }