hprox-0.7.0: src/Network/HProx/Auth.hs
-- SPDX-License-Identifier: Apache-2.0
--
-- Copyright (C) 2026 Bin Jin. All Rights Reserved.
module Network.HProx.Auth
( loadProxyAuth
) where
import Control.Monad (when)
import Data.ByteString.Char8 qualified as BS8
import Data.HashMap.Strict qualified as HM
import Data.Maybe (catMaybes)
import Network.HProx.Log
import Network.HProx.Util
loadProxyAuth :: Logger -> Maybe FilePath -> IO (Maybe (BS8.ByteString -> Bool))
loadProxyAuth _ Nothing = return Nothing
loadProxyAuth logger (Just f) = do
logger INFO $ "read username and passwords from " <> toLogStr f
userList <- zip [1 :: Int ..] . map stripTrailingCarriageReturn . BS8.lines <$> BS8.readFile f
let anyPlaintext = any (\(_, line) -> length (BS8.elemIndices ':' line) /= 2) userList
processUser (lineNumber, userpass) = case passwordReader userpass of
Nothing -> do
logger WARN $ "unable to parse line " <> toLogStr lineNumber <> malformedLineUserContext userpass <> " from password file"
return Nothing
Just (user, pass) -> do
salted <- hashPasswordWithRandomSalt pass
logger TRACE $ "parsed user (with salted password) from password file: " <> toLogStr (passwordWriter user salted)
return $ Just (user, salted)
passwordByUser <- HM.fromList . catMaybes <$> mapM processUser userList
when anyPlaintext $ do
logger INFO $ "writing back to password file " <> toLogStr f
BS8.writeFile f (BS8.unlines [ passwordWriter u p | (u, p) <- HM.toList passwordByUser])
let verify line = do
idx <- BS8.elemIndex ':' line
let user = BS8.take idx line
pass = BS8.drop (idx + 1) line
targetPass <- HM.lookup user passwordByUser
return $ verifyPassword targetPass pass
return $ Just (\line -> verify line == Just True)
stripTrailingCarriageReturn :: BS8.ByteString -> BS8.ByteString
stripTrailingCarriageReturn line
| "\r" `BS8.isSuffixOf` line = BS8.init line
| otherwise = line
malformedLineUserContext :: BS8.ByteString -> LogStr
malformedLineUserContext line =
case BS8.elemIndex ':' line of
Nothing -> ""
Just 0 -> ""
Just i -> " for user " <> toLogStr (BS8.take i line)