Make maintenance logins resilient to LDAP failures

This commit is contained in:
Gregor Kleen 2018-08-02 14:38:32 +02:00
parent 99354919ba
commit b4f08c28df
3 changed files with 51 additions and 16 deletions

View File

@ -87,6 +87,7 @@ dependencies:
- gitrev
- Glob
- ldap-client
- connection
# The library contains all of our application code. The executable
# defined below is just a thin wrapper.

View File

@ -10,6 +10,7 @@
module Auth.LDAP
( campusLogin
, CampusUserException(..)
, campusUser
, CampusMessage(..)
, Ldap.AttrList, Ldap.Attr(..), Ldap.AttrValue
@ -17,6 +18,9 @@ module Auth.LDAP
import Import.NoFoundation
import Control.Lens
import Network.Connection
import qualified Control.Monad.Catch as Exc
import Utils.Form
@ -108,6 +112,9 @@ campusLogin conf@LdapConf{..} = AuthPlugin{..}
$(widgetFile "widgets/campus-login-form")
data CampusUserException = CampusUserLdapError Ldap.LdapError
| CampusUserHostNotResolved String
| CampusUserLineTooLong
| CampusUserHostCannotConnect String [IOException]
| CampusUserNoResult
| CampusUserAmbiguous
deriving (Show, Eq, Typeable)
@ -115,7 +122,7 @@ data CampusUserException = CampusUserLdapError Ldap.LdapError
instance Exception CampusUserException
campusUser :: (MonadIO m, MonadThrow m) => LdapConf -> Creds site -> m (Ldap.AttrList [])
campusUser conf@LdapConf{..} Creds{..} = liftIO $ either (throwM . CampusUserLdapError) return <=< Ldap.with ldapHost ldapPort $ \ldap -> do
campusUser conf@LdapConf{..} Creds{..} = liftIO . (`catches` errHandlers) $ either (throwM . CampusUserLdapError) return <=< Ldap.with ldapHost ldapPort $ \ldap -> do
Ldap.bind ldap ldapDn ldapPassword
results <- case lookup "DN" credsExtra of
Just userDN -> do
@ -133,6 +140,11 @@ campusUser conf@LdapConf{..} Creds{..} = liftIO $ either (throwM . CampusUserLda
[] -> throwM CampusUserNoResult
[Ldap.SearchEntry _ attrs] -> return attrs
_otherwise -> throwM CampusUserAmbiguous
where
errHandlers = [ Exc.Handler $ \LineTooLong -> throwM CampusUserLineTooLong
, Exc.Handler $ \(HostNotResolved host) -> throwM $ CampusUserHostNotResolved host
, Exc.Handler $ \(HostCannotConnect host excs) -> throwM $ CampusUserHostCannotConnect host excs
]
-- ldapConfig :: UniWorX -> LDAPConfig
-- ldapConfig _app@(appSettings -> settings) = LDAPConfig

View File

@ -70,6 +70,7 @@ import Control.Monad.Trans.Reader (runReader)
import Control.Monad.Trans.Writer (WriterT(..))
import Control.Monad.Writer.Class (MonadWriter(..))
import Control.Monad.Catch (handleAll)
import qualified Control.Monad.Catch as C
import System.FilePath
@ -1076,23 +1077,45 @@ instance YesodAuth UniWorX where
setTitleI MsgLoginTitle
$(widgetFile "login")
authenticate creds@(Creds{..}) = runDB . fmap (either id id) . runExceptT $ do
let (userPlugin, userIdent)
| isDummy
, [dummyPlugin, dummyIdent] <- Text.splitOn ":" credsIdent
= (dummyPlugin, dummyIdent)
| otherwise
= (credsPlugin, credsIdent)
isDummy = credsPlugin == "dummy"
isPWFile = credsPlugin == "PWFile"
uAuth = UniqueAuthentication userPlugin userIdent
authenticate Creds{..} = runDB $ do
let
(userPlugin, userIdent)
| isDummy
, [dummyPlugin, dummyIdent] <- Text.splitOn ":" credsIdent
= (dummyPlugin, dummyIdent)
| otherwise
= (credsPlugin, credsIdent)
isDummy = credsPlugin == "dummy"
isPWFile = credsPlugin == "PWFile"
uAuth = UniqueAuthentication userPlugin userIdent
$logDebugS "auth" $ tshow creds
excHandlers
| isDummy || isPWFile
= [ C.Handler $ \err -> do
addMessage "error" (toHtml $ tshow (err :: CampusUserException))
$logErrorS "LDAP" $ tshow err
acceptExisting
]
| otherwise
= [ C.Handler $ \case
CampusUserNoResult -> do
$logWarnS "LDAP" $ "User lookup failed after successful login for " <> credsIdent
return . UserError $ IdentifierNotFound credsIdent
CampusUserAmbiguous -> do
$logWarnS "LDAP" $ "Multiple LDAP results for " <> credsIdent
return . UserError $ IdentifierNotFound credsIdent
err -> do
$logErrorS "LDAP" $ tshow err
return $ ServerError "LDAP lookup failed"
]
acceptExisting = maybe (UserError $ IdentifierNotFound credsIdent) (Authenticated . entityKey) <$> getBy uAuth
$logDebugS "auth" $ tshow Creds{..}
AppSettings{..} <- getsYesod appSettings
case appLdapConf of
Just ldapConf -> do
flip catches excHandlers $ case appLdapConf of
Just ldapConf -> fmap (either id id) . runExceptT $ do
ldapData <- campusUser ldapConf $ Creds userPlugin userIdent credsExtra
$logDebugS "LDAP" $ "Successful LDAP lookup: " <> tshow ldapData
@ -1155,8 +1178,7 @@ instance YesodAuth UniWorX where
lift $ insertMany_ fs
return $ Authenticated userId
Nothing -> (throwError =<<) . lift $
maybe (UserError $ IdentifierNotFound credsIdent) (Authenticated . entityKey) <$> getBy uAuth
Nothing -> acceptExisting
where
insertMaybe key val = get key >>= maybe (insert_ val) (\_ -> return ())