Support git-push over SSH using the git binary

This commit is contained in:
fr33domlover 2016-04-30 16:23:34 +00:00
parent 4065143d8f
commit f7a9fb6ac8

View file

@ -68,7 +68,10 @@ data RepoSpec
| SpecRepo Text | SpecRepo Text
deriving Show deriving Show
data Action = UploadPack RepoSpec deriving Show data Action
= UploadPack RepoSpec
| ReceivePack RepoSpec
deriving Show
-- | Result of running an action on the server side as a response to an SSH -- | Result of running an action on the server side as a response to an SSH
-- channel request. -- channel request.
@ -137,7 +140,8 @@ repoSpecP = f <$> (msh *> part) <*> optional (char '/' *> part)
msh = optional (satisfy $ \ c -> c == '/' || c == '~') msh = optional (satisfy $ \ c -> c == '/' || c == '~')
actionP :: Parser Action actionP :: Parser Action
actionP = UploadPack <$> ("git-upload-pack '" *> repoSpecP <* char '\'') actionP = UploadPack <$> ("git-upload-pack '" *> repoSpecP <* char '\'')
<|> ReceivePack <$> ("git-receive-pack '" *> repoSpecP <* char '\'')
parseExec :: Text -> Either String Action parseExec :: Text -> Either String Action
parseExec input = parseOnly (actionP <* endOfInput) input parseExec input = parseOnly (actionP <* endOfInput) input
@ -155,6 +159,12 @@ resolveSpec (SpecRepo r) = do
u <- T.pack . authUser <$> askAuthDetails u <- T.pack . authUser <$> askAuthDetails
return (u, r) return (u, r)
resolveSpec' :: FilePath -> RepoSpec -> Channel (Text, Text, FilePath)
resolveSpec' repoDir spec = do
(u, r) <- resolveSpec spec
let repoPath = repoDir </> T.unpack u </> T.unpack r
return (u, r, repoPath)
execute :: FilePath -> [String] -> Channel () execute :: FilePath -> [String] -> Channel ()
execute cmd args = do execute cmd args = do
lift $ $logDebugS src $ lift $ $logDebugS src $
@ -170,16 +180,34 @@ execute cmd args = do
(verifyPipe mIn, verifyPipe mOut, verifyPipe mErr, ph) (verifyPipe mIn, verifyPipe mOut, verifyPipe mErr, ph)
spawnProcess $ verifyPipes <$> createProcess config spawnProcess $ verifyPipes <$> createProcess config
whenRepoExists :: FilePath -> Channel ActionResult -> Channel ActionResult
whenRepoExists repoPath action = do
looksGood <- liftIO $ isRepo $ fromString repoPath
if looksGood
then action
else return $ ARFail "No such git repository"
runAction :: FilePath -> Bool -> Action -> Channel ActionResult runAction :: FilePath -> Bool -> Action -> Channel ActionResult
runAction repoDir _wantReply action = runAction repoDir _wantReply action =
case action of case action of
UploadPack spec -> do UploadPack spec -> do
(sharer, repo) <- resolveSpec spec (sharer, repo, repoPath) <- resolveSpec' repoDir spec
let repoPath = repoDir </> T.unpack sharer </> T.unpack repo whenRepoExists repoPath $ do
looksGood <- liftIO $ isRepo $ fromString repoPath execute "git-upload-pack" [repoPath]
if looksGood return ARProcess
then execute "git-upload-pack" [repoPath] >> return ARProcess ReceivePack spec -> do
else return $ ARFail "No such git repository" (sharer, repo, repoPath) <- resolveSpec' repoDir spec
-- Now we need to check whether the authenticated user (can get its
-- details with 'askAuthDetails') has write access to the repo.
-- This is currently true iff the authenticated user and the repo
-- sharer have the same ID. Since sharer names are unique, it's
-- enough to compare them.
userName <- T.pack . authUser <$> askAuthDetails
if userName == sharer
then whenRepoExists repoPath $ do
execute "git-receive-pack" [repoPath]
return ARProcess
else return $ ARFail "You can't push to this repository"
handle :: FilePath -> Bool -> ChannelRequest -> Channel () handle :: FilePath -> Bool -> ChannelRequest -> Channel ()
handle repoDir wantReply request = do handle repoDir wantReply request = do