feat(metrics): observe login attempts
This commit is contained in:
parent
19b8b0616f
commit
0c7e56f405
@ -6,6 +6,7 @@ module Auth.Dummy
|
|||||||
import Import.NoFoundation
|
import Import.NoFoundation
|
||||||
import Database.Persist.Sql (SqlBackendCanRead)
|
import Database.Persist.Sql (SqlBackendCanRead)
|
||||||
|
|
||||||
|
import Utils.Metrics
|
||||||
import Utils.Form
|
import Utils.Form
|
||||||
|
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.CaseInsensitive as CI
|
||||||
@ -56,7 +57,8 @@ dummyLogin = AuthPlugin{..}
|
|||||||
FormMissing -> do
|
FormMissing -> do
|
||||||
addMessageI Warning MsgDummyNoFormData
|
addMessageI Warning MsgDummyNoFormData
|
||||||
redirect $ tp LoginR
|
redirect $ tp LoginR
|
||||||
FormSuccess ident ->
|
FormSuccess ident -> do
|
||||||
|
observeLoginOutcome apName LoginSuccessful
|
||||||
setCredsRedirect $ Creds apName (CI.original ident) []
|
setCredsRedirect $ Creds apName (CI.original ident) []
|
||||||
apDispatch _ [] = badMethod
|
apDispatch _ [] = badMethod
|
||||||
apDispatch _ _ = notFound
|
apDispatch _ _ = notFound
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import Import.NoFoundation
|
|||||||
|
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.CaseInsensitive as CI
|
||||||
|
|
||||||
|
import Utils.Metrics
|
||||||
import Utils.Form
|
import Utils.Form
|
||||||
|
|
||||||
import qualified Ldap.Client as Ldap
|
import qualified Ldap.Client as Ldap
|
||||||
@ -196,15 +197,19 @@ campusLogin pool mode = AuthPlugin{..}
|
|||||||
Left err
|
Left err
|
||||||
| LdapError (Ldap.ResponseError (Ldap.ResponseErrorCode _ Ldap.InvalidCredentials _ _)) <- err
|
| LdapError (Ldap.ResponseError (Ldap.ResponseErrorCode _ Ldap.InvalidCredentials _ _)) <- err
|
||||||
-> do
|
-> do
|
||||||
$logDebugS "LDAP" "Invalid credentials"
|
$logDebugS apName "Invalid credentials"
|
||||||
|
observeLoginOutcome apName LoginInvalidCredentials
|
||||||
loginErrorMessageI LoginR Msg.InvalidLogin
|
loginErrorMessageI LoginR Msg.InvalidLogin
|
||||||
| otherwise -> do
|
| otherwise -> do
|
||||||
$logErrorS "LDAP" $ "Error during login: " <> tshow err
|
$logErrorS apName $ "Error during login: " <> tshow err
|
||||||
|
observeLoginOutcome apName LoginError
|
||||||
loginErrorMessageI LoginR Msg.AuthError
|
loginErrorMessageI LoginR Msg.AuthError
|
||||||
Right (Right (userDN, credsIdent)) ->
|
Right (Right (userDN, credsIdent)) -> do
|
||||||
|
observeLoginOutcome apName LoginSuccessful
|
||||||
setCredsRedirect $ Creds apName credsIdent [("DN", userDN)]
|
setCredsRedirect $ Creds apName credsIdent [("DN", userDN)]
|
||||||
Right (Left searchResults) -> do
|
Right (Left searchResults) -> do
|
||||||
$logWarnS "LDAP" $ "Could not extract principal name: " <> tshow searchResults
|
$logWarnS apName $ "Could not extract principal name: " <> tshow searchResults
|
||||||
|
observeLoginOutcome apName LoginError
|
||||||
loginErrorMessageI LoginR Msg.AuthError
|
loginErrorMessageI LoginR Msg.AuthError
|
||||||
|
|
||||||
maybe (redirect $ tp LoginR) return resp
|
maybe (redirect $ tp LoginR) return resp
|
||||||
@ -216,7 +221,7 @@ campusLogin pool mode = AuthPlugin{..}
|
|||||||
(login, loginEnctype) <- handlerToWidget . generateFormPost $ renderWForm FormStandard campusForm
|
(login, loginEnctype) <- handlerToWidget . generateFormPost $ renderWForm FormStandard campusForm
|
||||||
let loginForm = wrapForm login FormSettings
|
let loginForm = wrapForm login FormSettings
|
||||||
{ formMethod = POST
|
{ formMethod = POST
|
||||||
, formAction = Just . SomeRoute . toMaster $ PluginR "LDAP" []
|
, formAction = Just . SomeRoute . toMaster $ PluginR apName []
|
||||||
, formEncoding = loginEnctype
|
, formEncoding = loginEnctype
|
||||||
, formAttrs = [("uw-no-navigate-away-prompt","")]
|
, formAttrs = [("uw-no-navigate-away-prompt","")]
|
||||||
, formSubmit = FormSubmit
|
, formSubmit = FormSubmit
|
||||||
|
|||||||
@ -6,6 +6,7 @@ module Auth.PWHash
|
|||||||
import Import.NoFoundation
|
import Import.NoFoundation
|
||||||
import Database.Persist.Sql (SqlBackendCanRead)
|
import Database.Persist.Sql (SqlBackendCanRead)
|
||||||
|
|
||||||
|
import Utils.Metrics
|
||||||
import Utils.Form
|
import Utils.Form
|
||||||
|
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.CaseInsensitive as CI
|
||||||
@ -63,10 +64,12 @@ hashLogin pwHashAlgo = AuthPlugin{..}
|
|||||||
user <- liftHandler . runDB . getBy $ UniqueAuthentication hashIdent
|
user <- liftHandler . runDB . getBy $ UniqueAuthentication hashIdent
|
||||||
case user of
|
case user of
|
||||||
Just (Entity _ User{ userAuthentication = AuthPWHash{..}, userIdent = CI.original -> userIdent })
|
Just (Entity _ User{ userAuthentication = AuthPWHash{..}, userIdent = CI.original -> userIdent })
|
||||||
| verifyPasswordWith pwHashAlgo (2^) (encodeUtf8 hashPassword) (encodeUtf8 authPWHash) -> -- (2^) is magic.
|
| verifyPasswordWith pwHashAlgo (2^) (encodeUtf8 hashPassword) (encodeUtf8 authPWHash) -> do -- (2^) is magic.
|
||||||
|
observeLoginOutcome apName LoginSuccessful
|
||||||
setCredsRedirect $ Creds apName userIdent []
|
setCredsRedirect $ Creds apName userIdent []
|
||||||
other -> do
|
other -> do
|
||||||
$logDebugS "PWHash" $ tshow other
|
$logDebugS apName $ tshow other
|
||||||
|
observeLoginOutcome apName LoginInvalidCredentials
|
||||||
loginErrorMessageI LoginR Msg.InvalidLogin
|
loginErrorMessageI LoginR Msg.InvalidLogin
|
||||||
|
|
||||||
maybe (redirect $ tp LoginR) return resp
|
maybe (redirect $ tp LoginR) return resp
|
||||||
|
|||||||
@ -6,6 +6,7 @@ module Utils.Metrics
|
|||||||
, withJobWorkerStateLbls
|
, withJobWorkerStateLbls
|
||||||
, observeYesodCacheSize
|
, observeYesodCacheSize
|
||||||
, observeFavouritesQuickActionsDuration
|
, observeFavouritesQuickActionsDuration
|
||||||
|
, LoginOutcome(..), observeLoginOutcome
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Import.NoFoundation hiding (Vector, Info)
|
import Import.NoFoundation hiding (Vector, Info)
|
||||||
@ -100,6 +101,12 @@ favouritesQuickActionsDuration = unsafeRegister $ histogram info buckets
|
|||||||
"Duration of time needed to calculate a set of favourite quick actions"
|
"Duration of time needed to calculate a set of favourite quick actions"
|
||||||
buckets = histogramBuckets 500e-6 50
|
buckets = histogramBuckets 500e-6 50
|
||||||
|
|
||||||
|
{-# NOINLINE loginOutcomes #-}
|
||||||
|
loginOutcomes :: Vector Label2 Counter
|
||||||
|
loginOutcomes = unsafeRegister . vector ("plugin", "outcome") $ counter info
|
||||||
|
where info = Info "uni2work_login_attempts_total"
|
||||||
|
"Number of login attempts"
|
||||||
|
|
||||||
|
|
||||||
withHealthReportMetrics :: MonadIO m => m HealthReport -> m HealthReport
|
withHealthReportMetrics :: MonadIO m => m HealthReport -> m HealthReport
|
||||||
withHealthReportMetrics act = do
|
withHealthReportMetrics act = do
|
||||||
@ -171,3 +178,18 @@ observeFavouritesQuickActionsDuration act = do
|
|||||||
liftIO . observe favouritesQuickActionsDuration . realToFrac $ end - start
|
liftIO . observe favouritesQuickActionsDuration . realToFrac $ end - start
|
||||||
|
|
||||||
either throwM return res
|
either throwM return res
|
||||||
|
|
||||||
|
data LoginOutcome
|
||||||
|
= LoginSuccessful
|
||||||
|
| LoginInvalidCredentials
|
||||||
|
| LoginError
|
||||||
|
deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable)
|
||||||
|
deriving anyclass (Universe, Finite)
|
||||||
|
nullaryPathPiece ''LoginOutcome $ camelToPathPiece' 1
|
||||||
|
|
||||||
|
observeLoginOutcome :: MonadHandler m
|
||||||
|
=> Text -- ^ Plugin
|
||||||
|
-> LoginOutcome
|
||||||
|
-> m ()
|
||||||
|
observeLoginOutcome plugin outcome
|
||||||
|
= liftIO $ withLabel loginOutcomes (plugin, toPathPiece outcome) incCounter
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user