1
0
Fork 0
mirror of https://code.sup39.dev/repos/Wqawg synced 2025-01-09 07:06:46 +09:00
vervis/src/Vervis/Ssh.hs

157 lines
4.8 KiB
Haskell
Raw Normal View History

2016-03-06 20:58:48 +09:00
{- This file is part of Vervis.
-
- Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
-
- Copying is an act of love. Please copy, reuse and share.
-
- The author(s) have dedicated all copyright and related and neighboring
- rights to this software to the public domain worldwide. This software is
- distributed without any warranty.
-
- You should have received a copy of the CC0 Public Domain Dedication along
- with this software. If not, see
- <http://creativecommons.org/publicdomain/zero/1.0/>.
-}
module Vervis.Ssh
( runSsh
)
where
import Prelude
import Control.Monad (when)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Logger
2016-03-06 20:58:48 +09:00
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Reader (ReaderT (runReaderT), ask)
import Data.ByteString (ByteString)
2016-03-06 20:58:48 +09:00
import Data.ByteString.Lazy (fromStrict)
import Data.Foldable (find)
import Data.Text (Text, pack, unpack)
2016-03-06 20:58:48 +09:00
import Database.Persist
import Database.Persist.Sql
2016-03-06 20:58:48 +09:00
import Network.SSH
import Network.SSH.Channel
import Network.SSH.Crypto
import Network.SSH.Session
--import Yesod.Default.Main (LogFunc)
2016-03-06 20:58:48 +09:00
import Vervis.Model
import Vervis.Settings
-- TODO:
-- [ ] See which git commands gitolite SSH supports and see if I can implement
-- them with Hit (i think it was git upload-pack)
type ChannelBase = LoggingT (ReaderT ConnectionPool IO)
type SessionBase = LoggingT (ReaderT ConnectionPool IO)
--type UserAuthId = PersonId
2016-03-06 20:58:48 +09:00
type Channel = ChannelT {-UserAuthId-} ChannelBase
type Session = SessionT SessionBase {-UserAuthId-} ChannelBase
type SshChanDB = SqlPersistT Channel
type SshSessDB = SqlPersistT Session
2016-03-06 20:58:48 +09:00
src :: Text
src = "SSH"
2016-03-06 20:58:48 +09:00
runChanDB :: SshChanDB a -> Channel a
runChanDB action = do
pool <- lift . lift $ ask
2016-03-06 20:58:48 +09:00
runSqlPool action pool
runSessDB :: SshSessDB a -> Session a
runSessDB action = do
pool <- lift . lift $ ask
2016-03-06 20:58:48 +09:00
runSqlPool action pool
chanFail :: Bool -> Text -> Channel ()
2016-03-06 20:58:48 +09:00
chanFail wantReply msg = do
channelError $ unpack msg
when wantReply channelFail
authorize :: Authorize -> Session Bool -- (AuthResult UserAuthId)
authorize (Password _ _) = return False -- AuthFail
2016-03-06 20:58:48 +09:00
authorize (PublicKey name key) = do
mpk <- runSessDB $ do
2016-03-06 20:58:48 +09:00
mp <- getBy $ UniquePersonLogin $ pack name
case mp of
Nothing -> return Nothing
Just (Entity pid _p) -> do
ks <- selectList [SshKeyPerson ==. pid] []
return $ Just (pid, ks)
case mpk of
2016-03-06 20:58:48 +09:00
Nothing -> do
$logInfoS src "Auth failed: Invalid user"
return False -- AuthFail
Just (pid, keys) -> do
2016-03-06 20:58:48 +09:00
let eValue (Entity _ v) = v
matches =
(== key) . blobToKey . fromStrict . sshKeyContent . eValue
case find matches keys of
Nothing -> do
$logInfoS src "Auth failed: No matching key found"
return False -- AuthFail
2016-03-06 20:58:48 +09:00
Just match -> do
$logInfoS src "Auth succeeded"
return True -- $ AuthSuccess pid
2016-03-06 20:58:48 +09:00
data Action = UploadPack () deriving Show
detectAction :: ChannelRequest -> Maybe Action
detectAction _ = Nothing
runAction :: Bool -> Action -> Channel (Maybe Text)
runAction _wantReply action =
case action of
UploadPack repo -> return $ Just "Doesn't work yet"
2016-03-06 20:58:48 +09:00
handle :: Bool -> ChannelRequest -> Channel ()
handle wantReply request = do
$logDebugS src $ pack $ show request
case detectAction request of
Nothing -> err "Unsupported request"
Just act -> do
$logDebugS src $ pack $ show act
res <- runAction wantReply act
case res of
Nothing -> do
when wantReply channelSuccess
channelDone
Just msg -> err msg
where
err = chanFail wantReply
2016-03-06 20:58:48 +09:00
ready :: LogFunc -> IO ()
ready = runLoggingT $ $logInfoS src "SSH server component starting"
2016-03-06 20:58:48 +09:00
mkConfig
:: AppSettings
-> ConnectionPool
-> LogFunc
-> IO (Config SessionBase ChannelBase {-UserAuthId-})
mkConfig settings pool logFunc = do
2016-03-06 20:58:48 +09:00
keyPair <- keyPairFromFile $ appSshKeyFile settings
return $ Config
{ cSession = SessionConfig
{ scAuthMethods = ["publickey"]
, scAuthorize = authorize
, scKeyPair = keyPair
, scRunBaseMonad =
flip runReaderT pool . flip runLoggingT logFunc
2016-03-06 20:58:48 +09:00
}
, cChannel = ChannelConfig
{ ccRequestHandler = handle
, ccRunBaseMonad =
flip runReaderT pool . flip runLoggingT logFunc
2016-03-06 20:58:48 +09:00
}
, cPort = fromIntegral $ appSshPort settings
, cReadyAction = ready logFunc
2016-03-06 20:58:48 +09:00
}
runSsh :: AppSettings -> ConnectionPool -> LogFunc -> IO ()
runSsh settings pool logFunc = do
config <- mkConfig settings pool logFunc
2016-03-06 20:58:48 +09:00
startConfig config