Parse the git-uploac-pack SSH command properly
This commit is contained in:
parent
77fd8333c6
commit
09775e02ae
3 changed files with 58 additions and 37 deletions
|
@ -38,30 +38,3 @@ import Data.Word
|
||||||
|
|
||||||
data RepoRef = RepoRef Text Text Text
|
data RepoRef = RepoRef Text Text Text
|
||||||
|
|
||||||
data RepoSpec
|
|
||||||
= SpecUserProjRepo Text Text Text
|
|
||||||
| SpecProjRepo Text Text
|
|
||||||
| SpecUserRepo Text Text
|
|
||||||
| SpecRepo Text
|
|
||||||
deriving Show
|
|
||||||
|
|
||||||
data Action = UploadPack RepoSpec deriving Show
|
|
||||||
|
|
||||||
repoSpecP :: Parser RepoSpec
|
|
||||||
repoSpecP =
|
|
||||||
SpecRepo <$> msep *> part
|
|
||||||
<|> SpecProjRepo <$> msep *> part <* sep <*> part
|
|
||||||
<|> SpecUserRepo <$> home *> part <* sep <*> part
|
|
||||||
<|> SpecUserProjRepo <$> msh *> part <* sep <*> part <* sep <*> part
|
|
||||||
where
|
|
||||||
part = takeWhile1 $ \ c -> c /= '/' && c /= '\''
|
|
||||||
sep = char '/'
|
|
||||||
msep = optional sep
|
|
||||||
home = char '~'
|
|
||||||
msh = optional $ satisfy $ \ c -> c == '/' || c == '~'
|
|
||||||
|
|
||||||
actionP :: Parser Action
|
|
||||||
actionP = UploadPack <$> ("git-upload-pack '" *> repoSpecP <* char '\'')
|
|
||||||
|
|
||||||
parseExec :: Text -> Either String Action
|
|
||||||
parseExec input = parseOnly (actionP <* endOfInput) input
|
|
||||||
|
|
|
@ -20,15 +20,17 @@ where
|
||||||
|
|
||||||
import Prelude
|
import Prelude
|
||||||
|
|
||||||
|
import Control.Applicative ((<|>), optional)
|
||||||
import Control.Monad (when)
|
import Control.Monad (when)
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Monad.IO.Class (liftIO)
|
||||||
import Control.Monad.Logger
|
import Control.Monad.Logger
|
||||||
import Control.Monad.Trans.Class (lift)
|
import Control.Monad.Trans.Class (lift)
|
||||||
import Control.Monad.Trans.Reader (ReaderT (runReaderT), ask)
|
import Control.Monad.Trans.Reader (ReaderT (runReaderT), ask)
|
||||||
|
import Data.Attoparsec.Text
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
import Data.ByteString.Lazy (fromStrict)
|
import Data.ByteString.Lazy (fromStrict)
|
||||||
import Data.Foldable (find)
|
import Data.Foldable (find)
|
||||||
import Data.Text (Text, pack, unpack)
|
import Data.Text (Text)
|
||||||
import Database.Persist
|
import Database.Persist
|
||||||
import Database.Persist.Sql
|
import Database.Persist.Sql
|
||||||
import Network.SSH
|
import Network.SSH
|
||||||
|
@ -36,9 +38,15 @@ import Network.SSH.Channel
|
||||||
import Network.SSH.Crypto
|
import Network.SSH.Crypto
|
||||||
import Network.SSH.Session
|
import Network.SSH.Session
|
||||||
|
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Types
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
type ChannelBase = LoggingT (ReaderT ConnectionPool IO)
|
type ChannelBase = LoggingT (ReaderT ConnectionPool IO)
|
||||||
type SessionBase = LoggingT (ReaderT ConnectionPool IO)
|
type SessionBase = LoggingT (ReaderT ConnectionPool IO)
|
||||||
type UserAuthId = PersonId
|
type UserAuthId = PersonId
|
||||||
|
@ -48,6 +56,17 @@ type Session = SessionT SessionBase UserAuthId ChannelBase
|
||||||
type SshChanDB = SqlPersistT Channel
|
type SshChanDB = SqlPersistT Channel
|
||||||
type SshSessDB = SqlPersistT Session
|
type SshSessDB = SqlPersistT Session
|
||||||
|
|
||||||
|
data RepoSpec
|
||||||
|
= SpecUserRepo Text Text
|
||||||
|
| SpecRepo Text
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
data Action = UploadPack RepoSpec deriving Show
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Utils
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
src :: Text
|
src :: Text
|
||||||
src = "SSH"
|
src = "SSH"
|
||||||
|
|
||||||
|
@ -63,14 +82,18 @@ runSessDB action = do
|
||||||
|
|
||||||
chanFail :: Bool -> Text -> Channel ()
|
chanFail :: Bool -> Text -> Channel ()
|
||||||
chanFail wantReply msg = do
|
chanFail wantReply msg = do
|
||||||
channelError $ unpack msg
|
channelError $ T.unpack msg
|
||||||
when wantReply channelFail
|
when wantReply channelFail
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Auth
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
authorize :: Authorize -> Session (AuthResult UserAuthId)
|
authorize :: Authorize -> Session (AuthResult UserAuthId)
|
||||||
authorize (Password _ _) = return AuthFail
|
authorize (Password _ _) = return AuthFail
|
||||||
authorize (PublicKey name key) = do
|
authorize (PublicKey name key) = do
|
||||||
mpk <- runSessDB $ do
|
mpk <- runSessDB $ do
|
||||||
mp <- getBy $ UniquePersonLogin $ pack name
|
mp <- getBy $ UniquePersonLogin $ T.pack name
|
||||||
case mp of
|
case mp of
|
||||||
Nothing -> return Nothing
|
Nothing -> return Nothing
|
||||||
Just (Entity pid _p) -> do
|
Just (Entity pid _p) -> do
|
||||||
|
@ -92,10 +115,30 @@ authorize (PublicKey name key) = do
|
||||||
lift $ $logInfoS src "Auth succeeded"
|
lift $ $logInfoS src "Auth succeeded"
|
||||||
return $ AuthSuccess pid
|
return $ AuthSuccess pid
|
||||||
|
|
||||||
data Action = UploadPack () deriving Show
|
-------------------------------------------------------------------------------
|
||||||
|
-- Actions
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
detectAction :: ChannelRequest -> Maybe Action
|
repoSpecP :: Parser RepoSpec
|
||||||
detectAction _ = Nothing
|
repoSpecP =
|
||||||
|
SpecRepo <$> (msh *> part)
|
||||||
|
<|> SpecUserRepo <$> (msh *> part) <* char '/' <*> part
|
||||||
|
where
|
||||||
|
part = takeWhile1 $ \ c -> c /= '/' && c /= '\''
|
||||||
|
msh = optional (satisfy $ \ c -> c == '/' || c == '~')
|
||||||
|
|
||||||
|
actionP :: Parser Action
|
||||||
|
actionP = UploadPack <$> ("git-upload-pack '" *> repoSpecP <* char '\'')
|
||||||
|
|
||||||
|
parseExec :: Text -> Either String Action
|
||||||
|
parseExec input = parseOnly (actionP <* endOfInput) input
|
||||||
|
|
||||||
|
detectAction :: ChannelRequest -> Either Text Action
|
||||||
|
detectAction (Execute s) =
|
||||||
|
case parseExec $ T.pack s of
|
||||||
|
Left _ -> Left "Unsupported command"
|
||||||
|
Right action -> Right action
|
||||||
|
detectAction _ = Left "Unsupported channel request"
|
||||||
|
|
||||||
runAction :: Bool -> Action -> Channel (Maybe Text)
|
runAction :: Bool -> Action -> Channel (Maybe Text)
|
||||||
runAction _wantReply action =
|
runAction _wantReply action =
|
||||||
|
@ -104,11 +147,11 @@ runAction _wantReply action =
|
||||||
|
|
||||||
handle :: Bool -> ChannelRequest -> Channel ()
|
handle :: Bool -> ChannelRequest -> Channel ()
|
||||||
handle wantReply request = do
|
handle wantReply request = do
|
||||||
lift $ $logDebugS src $ pack $ show request
|
lift $ $logDebugS src $ T.pack $ show request
|
||||||
case detectAction request of
|
case detectAction request of
|
||||||
Nothing -> err "Unsupported request"
|
Left e -> err e
|
||||||
Just act -> do
|
Right act -> do
|
||||||
lift $ $logDebugS src $ pack $ show act
|
lift $ $logDebugS src $ T.pack $ show act
|
||||||
res <- runAction wantReply act
|
res <- runAction wantReply act
|
||||||
case res of
|
case res of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
|
@ -118,6 +161,10 @@ handle wantReply request = do
|
||||||
where
|
where
|
||||||
err = chanFail wantReply
|
err = chanFail wantReply
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Config and running
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
ready :: LogFunc -> IO ()
|
ready :: LogFunc -> IO ()
|
||||||
ready = runLoggingT $ $logInfoS src "SSH server component starting"
|
ready = runLoggingT $ $logInfoS src "SSH server component starting"
|
||||||
|
|
||||||
|
|
|
@ -87,6 +87,7 @@ library
|
||||||
TupleSections
|
TupleSections
|
||||||
RecordWildCards
|
RecordWildCards
|
||||||
build-depends: aeson
|
build-depends: aeson
|
||||||
|
, attoparsec
|
||||||
, base
|
, base
|
||||||
, base64-bytestring
|
, base64-bytestring
|
||||||
, binary
|
, binary
|
||||||
|
|
Loading…
Reference in a new issue