packages feed

iptadmin-1.2.0: src/IptAdmin/AccessControl.hs

module IptAdmin.AccessControl where

import Control.Exception (catch, SomeException)
import Control.Monad.Error
import Control.Monad.State
import Data.IORef
import Data.Map
import Data.Maybe
import Happstack.Server.SimpleHTTP
import IptAdmin.LoginPage as LoginPage (pageHandlers)
import IptAdmin.Static as Static
import IptAdmin.Types
import IptAdmin.Utils
import Prelude hiding (catch)
import System.Posix.PAM as PAM
-- import System.Posix.Syslog
import System.Posix.User

-- verify that client is allowed to access server
authorize :: IORef Sessions -> IptAdminConfig -> IptAdmin Response -> IptAdminAuth Response
authorize sessionsIORef config requestHandler =
    -- allow 'static' dir without authorisation
    (dir "static" Static.pageHandlers) `mplus` do
        clientIdE <- getDataFn $ lookCookieValue "sessionId"
        isAuthorised <- case clientIdE of
            Left _ ->
                -- liftIO $ syslog Notice $ "sessionId cookie get error: " ++ show e
                return False
            Right a -> do
                sessions <- liftIO $ readIORef sessionsIORef
                let session = Data.Map.lookup a sessions
                case session of
                    Nothing ->
                        -- liftIO $ syslog Notice $ "session was not found: " ++ a
                        -- liftIO $ syslog Notice $ "sessions: " ++ show sessions
                        return False
                    Just _ -> return True
        if isAuthorised
            then
                -- Run IptAdmin monad with state
                mapServerPartT (addStateToStack (either undefined id clientIdE, sessionsIORef, config)) requestHandler
            else
                msum [ dir "login" $ LoginPage.pageHandlers (IptAdmin.AccessControl.authenticate $ cPamName config)
                                                            sessionsIORef
                     , redir "/login"
                     ]
    where
        addStateToStack :: (Monad m) => MainState
                           -> UnWebT (ErrorT String (StateT MainState m)) a
                           -> UnWebT (ErrorT String m) a
        addStateToStack mainState statefulAction =
            mapErrorT (addStateToStack' mainState) statefulAction

        addStateToStack' :: (Monad m) => MainState
                         -> StateT MainState m (Either String a)
                         -> m (Either String a)
        addStateToStack' mainState statefulAction =
            evalStateT statefulAction mainState

logout :: IptAdmin Response
logout = do
    (sessionId, sessionsIORef, _) <- lift get
    _ <- liftIO $ atomicModifyIORef sessionsIORef
                                    (\ m -> (delete sessionId m, ()))
    expireCookie "sessionId"
    redir "/"

{- Nothing - authentication is successful
-- or Just error message
-}
authenticate :: String -> String -> String -> IO (Maybe String)
authenticate pamName login password = do
    authRes <- PAM.authenticate pamName login password
    case authRes of
        Left a -> return $ Just $ "Pam message: " ++ pamCodeToMessage a
        Right () ->
            {- The user exists in system and password is correct
             - Next authentication stage: check if it's permitted to work with firewall for the user
             -}
            if login == "root"
                then return Nothing
                else do
                    groupEntryMay <- catch (Just `fmap` getGroupEntryForName "iptadmin")
                                           ((\ _ -> return Nothing) :: (SomeException -> IO (Maybe GroupEntry)))
                    case groupEntryMay of
                        Nothing -> return $ Just "The user is not allowed to access iptables"
                        Just groupEntry ->
                            if login `elem` groupMembers groupEntry
                                then return Nothing
                                else return $ Just "The user is not allowed to access iptables"