Make maintenance logins resilient to LDAP failures
This commit is contained in:
parent
99354919ba
commit
b4f08c28df
@ -87,6 +87,7 @@ dependencies:
|
|||||||
- gitrev
|
- gitrev
|
||||||
- Glob
|
- Glob
|
||||||
- ldap-client
|
- ldap-client
|
||||||
|
- connection
|
||||||
|
|
||||||
# The library contains all of our application code. The executable
|
# The library contains all of our application code. The executable
|
||||||
# defined below is just a thin wrapper.
|
# defined below is just a thin wrapper.
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
module Auth.LDAP
|
module Auth.LDAP
|
||||||
( campusLogin
|
( campusLogin
|
||||||
|
, CampusUserException(..)
|
||||||
, campusUser
|
, campusUser
|
||||||
, CampusMessage(..)
|
, CampusMessage(..)
|
||||||
, Ldap.AttrList, Ldap.Attr(..), Ldap.AttrValue
|
, Ldap.AttrList, Ldap.Attr(..), Ldap.AttrValue
|
||||||
@ -17,6 +18,9 @@ module Auth.LDAP
|
|||||||
|
|
||||||
import Import.NoFoundation
|
import Import.NoFoundation
|
||||||
import Control.Lens
|
import Control.Lens
|
||||||
|
import Network.Connection
|
||||||
|
|
||||||
|
import qualified Control.Monad.Catch as Exc
|
||||||
|
|
||||||
import Utils.Form
|
import Utils.Form
|
||||||
|
|
||||||
@ -108,6 +112,9 @@ campusLogin conf@LdapConf{..} = AuthPlugin{..}
|
|||||||
$(widgetFile "widgets/campus-login-form")
|
$(widgetFile "widgets/campus-login-form")
|
||||||
|
|
||||||
data CampusUserException = CampusUserLdapError Ldap.LdapError
|
data CampusUserException = CampusUserLdapError Ldap.LdapError
|
||||||
|
| CampusUserHostNotResolved String
|
||||||
|
| CampusUserLineTooLong
|
||||||
|
| CampusUserHostCannotConnect String [IOException]
|
||||||
| CampusUserNoResult
|
| CampusUserNoResult
|
||||||
| CampusUserAmbiguous
|
| CampusUserAmbiguous
|
||||||
deriving (Show, Eq, Typeable)
|
deriving (Show, Eq, Typeable)
|
||||||
@ -115,7 +122,7 @@ data CampusUserException = CampusUserLdapError Ldap.LdapError
|
|||||||
instance Exception CampusUserException
|
instance Exception CampusUserException
|
||||||
|
|
||||||
campusUser :: (MonadIO m, MonadThrow m) => LdapConf -> Creds site -> m (Ldap.AttrList [])
|
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
|
Ldap.bind ldap ldapDn ldapPassword
|
||||||
results <- case lookup "DN" credsExtra of
|
results <- case lookup "DN" credsExtra of
|
||||||
Just userDN -> do
|
Just userDN -> do
|
||||||
@ -133,6 +140,11 @@ campusUser conf@LdapConf{..} Creds{..} = liftIO $ either (throwM . CampusUserLda
|
|||||||
[] -> throwM CampusUserNoResult
|
[] -> throwM CampusUserNoResult
|
||||||
[Ldap.SearchEntry _ attrs] -> return attrs
|
[Ldap.SearchEntry _ attrs] -> return attrs
|
||||||
_otherwise -> throwM CampusUserAmbiguous
|
_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 :: UniWorX -> LDAPConfig
|
||||||
-- ldapConfig _app@(appSettings -> settings) = LDAPConfig
|
-- ldapConfig _app@(appSettings -> settings) = LDAPConfig
|
||||||
|
|||||||
@ -70,6 +70,7 @@ import Control.Monad.Trans.Reader (runReader)
|
|||||||
import Control.Monad.Trans.Writer (WriterT(..))
|
import Control.Monad.Trans.Writer (WriterT(..))
|
||||||
import Control.Monad.Writer.Class (MonadWriter(..))
|
import Control.Monad.Writer.Class (MonadWriter(..))
|
||||||
import Control.Monad.Catch (handleAll)
|
import Control.Monad.Catch (handleAll)
|
||||||
|
import qualified Control.Monad.Catch as C
|
||||||
|
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
|
|
||||||
@ -1076,23 +1077,45 @@ instance YesodAuth UniWorX where
|
|||||||
setTitleI MsgLoginTitle
|
setTitleI MsgLoginTitle
|
||||||
$(widgetFile "login")
|
$(widgetFile "login")
|
||||||
|
|
||||||
authenticate creds@(Creds{..}) = runDB . fmap (either id id) . runExceptT $ do
|
authenticate Creds{..} = runDB $ do
|
||||||
let (userPlugin, userIdent)
|
let
|
||||||
| isDummy
|
(userPlugin, userIdent)
|
||||||
, [dummyPlugin, dummyIdent] <- Text.splitOn ":" credsIdent
|
| isDummy
|
||||||
= (dummyPlugin, dummyIdent)
|
, [dummyPlugin, dummyIdent] <- Text.splitOn ":" credsIdent
|
||||||
| otherwise
|
= (dummyPlugin, dummyIdent)
|
||||||
= (credsPlugin, credsIdent)
|
| otherwise
|
||||||
isDummy = credsPlugin == "dummy"
|
= (credsPlugin, credsIdent)
|
||||||
isPWFile = credsPlugin == "PWFile"
|
isDummy = credsPlugin == "dummy"
|
||||||
uAuth = UniqueAuthentication userPlugin userIdent
|
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
|
AppSettings{..} <- getsYesod appSettings
|
||||||
|
|
||||||
case appLdapConf of
|
flip catches excHandlers $ case appLdapConf of
|
||||||
Just ldapConf -> do
|
Just ldapConf -> fmap (either id id) . runExceptT $ do
|
||||||
ldapData <- campusUser ldapConf $ Creds userPlugin userIdent credsExtra
|
ldapData <- campusUser ldapConf $ Creds userPlugin userIdent credsExtra
|
||||||
$logDebugS "LDAP" $ "Successful LDAP lookup: " <> tshow ldapData
|
$logDebugS "LDAP" $ "Successful LDAP lookup: " <> tshow ldapData
|
||||||
|
|
||||||
@ -1155,8 +1178,7 @@ instance YesodAuth UniWorX where
|
|||||||
|
|
||||||
lift $ insertMany_ fs
|
lift $ insertMany_ fs
|
||||||
return $ Authenticated userId
|
return $ Authenticated userId
|
||||||
Nothing -> (throwError =<<) . lift $
|
Nothing -> acceptExisting
|
||||||
maybe (UserError $ IdentifierNotFound credsIdent) (Authenticated . entityKey) <$> getBy uAuth
|
|
||||||
|
|
||||||
where
|
where
|
||||||
insertMaybe key val = get key >>= maybe (insert_ val) (\_ -> return ())
|
insertMaybe key val = get key >>= maybe (insert_ val) (\_ -> return ())
|
||||||
|
|||||||
Reference in New Issue
Block a user