Use the RBAC system to determine repo push access
This commit is contained in:
parent
2d4fb85fca
commit
d655e7302e
3 changed files with 68 additions and 33 deletions
|
@ -14,7 +14,8 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Form.Repo
|
module Vervis.Form.Repo
|
||||||
( newRepoForm
|
( NewRepo (..)
|
||||||
|
, newRepoForm
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -22,17 +23,26 @@ where
|
||||||
|
|
||||||
import Vervis.Import
|
import Vervis.Import
|
||||||
import Vervis.Field.Repo
|
import Vervis.Field.Repo
|
||||||
import Vervis.Model.Ident (prj2text, text2rp)
|
import Vervis.Model
|
||||||
|
import Vervis.Model.Ident
|
||||||
import Vervis.Model.Repo
|
import Vervis.Model.Repo
|
||||||
|
|
||||||
newRepoAForm :: SharerId -> Maybe ProjectId -> AForm Handler Repo
|
data NewRepo = NewRepo
|
||||||
newRepoAForm sid mpid = Repo
|
{ nrpIdent :: RpIdent
|
||||||
|
, nrpVcs :: VersionControlSystem
|
||||||
|
, nrpProj :: Maybe ProjectId
|
||||||
|
, nrpDesc :: Maybe Text
|
||||||
|
, nrpRole :: RoleId
|
||||||
|
}
|
||||||
|
|
||||||
|
newRepoAForm
|
||||||
|
:: PersonId -> SharerId -> Maybe ProjectId -> AForm Handler NewRepo
|
||||||
|
newRepoAForm pid sid mpid = NewRepo
|
||||||
<$> (text2rp <$> areq (mkIdentField sid) "Identifier*" Nothing)
|
<$> (text2rp <$> areq (mkIdentField sid) "Identifier*" Nothing)
|
||||||
<*> pure sid
|
|
||||||
<*> areq (selectFieldList vcsList) "Version control system*" Nothing
|
<*> areq (selectFieldList vcsList) "Version control system*" Nothing
|
||||||
<*> aopt selectProject "Project" (Just mpid)
|
<*> aopt selectProject "Project" (Just mpid)
|
||||||
<*> aopt textField "Description" Nothing
|
<*> aopt textField "Description" Nothing
|
||||||
<*> pure "master"
|
<*> areq selectRole "Your role*" Nothing
|
||||||
where
|
where
|
||||||
vcsList :: [(Text, VersionControlSystem)]
|
vcsList :: [(Text, VersionControlSystem)]
|
||||||
vcsList =
|
vcsList =
|
||||||
|
@ -43,6 +53,10 @@ newRepoAForm sid mpid = Repo
|
||||||
selectField $
|
selectField $
|
||||||
optionsPersistKey [ProjectSharer ==. sid] [Asc ProjectIdent] $
|
optionsPersistKey [ProjectSharer ==. sid] [Asc ProjectIdent] $
|
||||||
prj2text . projectIdent
|
prj2text . projectIdent
|
||||||
|
selectRole =
|
||||||
|
selectField $
|
||||||
|
optionsPersistKey [RolePerson ==. pid] [] $
|
||||||
|
rl2text . roleIdent
|
||||||
|
|
||||||
newRepoForm :: SharerId -> Maybe ProjectId -> Form Repo
|
newRepoForm :: PersonId -> SharerId -> Maybe ProjectId -> Form NewRepo
|
||||||
newRepoForm sid mpid = renderDivs $ newRepoAForm sid mpid
|
newRepoForm pid sid mpid = renderDivs $ newRepoAForm pid sid mpid
|
||||||
|
|
|
@ -109,20 +109,35 @@ getReposR user = do
|
||||||
|
|
||||||
postReposR :: ShrIdent -> Handler Html
|
postReposR :: ShrIdent -> Handler Html
|
||||||
postReposR user = do
|
postReposR user = do
|
||||||
Entity _pid person <- requireAuth
|
Entity pid person <- requireAuth
|
||||||
let sid = personIdent person
|
Entity sid _sharer <- runDB $ getBy404 $ UniqueSharer user
|
||||||
((result, widget), enctype) <- runFormPost $ newRepoForm sid Nothing
|
((result, widget), enctype) <- runFormPost $ newRepoForm pid sid Nothing
|
||||||
case result of
|
case result of
|
||||||
FormSuccess repo -> do
|
FormSuccess nrp -> do
|
||||||
parent <- askSharerDir user
|
parent <- askSharerDir user
|
||||||
liftIO $ do
|
liftIO $ do
|
||||||
createDirectoryIfMissing True parent
|
createDirectoryIfMissing True parent
|
||||||
let repoName =
|
let repoName =
|
||||||
unpack $ CI.foldedCase $ unRpIdent $ repoIdent repo
|
unpack $ CI.foldedCase $ unRpIdent $ nrpIdent nrp
|
||||||
case repoVcs repo of
|
case nrpVcs nrp of
|
||||||
VCSDarcs -> D.createRepo parent repoName
|
VCSDarcs -> D.createRepo parent repoName
|
||||||
VCSGit -> G.createRepo parent repoName
|
VCSGit -> G.createRepo parent repoName
|
||||||
runDB $ insert_ repo
|
runDB $ do
|
||||||
|
let repo = Repo
|
||||||
|
{ repoIdent = nrpIdent nrp
|
||||||
|
, repoSharer = sid
|
||||||
|
, repoVcs = nrpVcs nrp
|
||||||
|
, repoProject = nrpProj nrp
|
||||||
|
, repoDesc = nrpDesc nrp
|
||||||
|
, repoMainBranch = "master"
|
||||||
|
}
|
||||||
|
rid <- insert repo
|
||||||
|
let collab = Collab
|
||||||
|
{ collabRepo = rid
|
||||||
|
, collabPerson = pid
|
||||||
|
, collabRole = nrpRole nrp
|
||||||
|
}
|
||||||
|
insert_ collab
|
||||||
setMessage "Repo added."
|
setMessage "Repo added."
|
||||||
redirect $ ReposR user
|
redirect $ ReposR user
|
||||||
FormMissing -> do
|
FormMissing -> do
|
||||||
|
@ -134,9 +149,9 @@ postReposR user = do
|
||||||
|
|
||||||
getRepoNewR :: ShrIdent -> Handler Html
|
getRepoNewR :: ShrIdent -> Handler Html
|
||||||
getRepoNewR user = do
|
getRepoNewR user = do
|
||||||
Entity _pid person <- requireAuth
|
Entity pid person <- requireAuth
|
||||||
let sid = personIdent person
|
Entity sid _sharer <- runDB $ getBy404 $ UniqueSharer user
|
||||||
((_result, widget), enctype) <- runFormPost $ newRepoForm sid Nothing
|
((_result, widget), enctype) <- runFormPost $ newRepoForm pid sid Nothing
|
||||||
defaultLayout $(widgetFile "repo/new")
|
defaultLayout $(widgetFile "repo/new")
|
||||||
|
|
||||||
selectRepo :: ShrIdent -> RpIdent -> AppDB Repo
|
selectRepo :: ShrIdent -> RpIdent -> AppDB Repo
|
||||||
|
|
|
@ -25,18 +25,20 @@ 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.Maybe (MaybeT (MaybeT, runMaybeT))
|
||||||
import Control.Monad.Trans.Reader (ReaderT (runReaderT), ask)
|
import Control.Monad.Trans.Reader (ReaderT (runReaderT), ask)
|
||||||
import Data.Attoparsec.Text
|
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.Git.Storage (isRepo)
|
import Data.Git.Storage (isRepo)
|
||||||
|
import Data.Maybe (isJust)
|
||||||
import Data.Monoid ((<>))
|
import Data.Monoid ((<>))
|
||||||
import Data.String (fromString)
|
import Data.String (fromString)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Formatting ((%))
|
|
||||||
import Database.Persist
|
import Database.Persist
|
||||||
import Database.Persist.Sql
|
import Database.Persist.Sql
|
||||||
|
import Formatting ((%))
|
||||||
import Network.SSH
|
import Network.SSH
|
||||||
import Network.SSH.Channel
|
import Network.SSH.Channel
|
||||||
import Network.SSH.Crypto
|
import Network.SSH.Crypto
|
||||||
|
@ -49,6 +51,8 @@ import qualified Data.Text as T
|
||||||
import qualified Formatting as F
|
import qualified Formatting as F
|
||||||
|
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
|
import Vervis.Model.Ident
|
||||||
|
import Vervis.Model.Role
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
@ -214,6 +218,18 @@ whenGitRepoExists repoPath action = do
|
||||||
then action
|
then action
|
||||||
else return $ ARFail "No such git repository"
|
else return $ ARFail "No such git repository"
|
||||||
|
|
||||||
|
canPushTo :: Text -> Text -> Channel Bool
|
||||||
|
canPushTo shr' rp' = do
|
||||||
|
let shr = text2shr shr'
|
||||||
|
rp = text2rp rp'
|
||||||
|
pid <- authId <$> askAuthDetails
|
||||||
|
ma <- runChanDB $ runMaybeT $ do
|
||||||
|
Entity sid _sharer <- MaybeT $ getBy $ UniqueSharer shr
|
||||||
|
Entity rid _repo <- MaybeT $ getBy $ UniqueRepo rp sid
|
||||||
|
Entity _cid collab <- MaybeT $ getBy $ UniqueCollab rid pid
|
||||||
|
MaybeT $ getBy $ UniqueAccess (collabRole collab) OpRepoPush
|
||||||
|
return $ isJust ma
|
||||||
|
|
||||||
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
|
||||||
|
@ -224,13 +240,8 @@ runAction repoDir _wantReply action =
|
||||||
return ARProcess
|
return ARProcess
|
||||||
DarcsApply spec -> do
|
DarcsApply spec -> do
|
||||||
(sharer, repo, repoPath) <- resolveSpec' repoDir spec
|
(sharer, repo, repoPath) <- resolveSpec' repoDir spec
|
||||||
-- Now we need to check whether the authenticated user (can get its
|
can <- canPushTo sharer repo
|
||||||
-- details with 'askAuthDetails') has write access to the repo.
|
if can
|
||||||
-- 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 whenDarcsRepoExists repoPath $ do
|
then whenDarcsRepoExists repoPath $ do
|
||||||
execute "darcs" ["apply", "--all", "--repodir", repoPath]
|
execute "darcs" ["apply", "--all", "--repodir", repoPath]
|
||||||
return ARProcess
|
return ARProcess
|
||||||
|
@ -242,13 +253,8 @@ runAction repoDir _wantReply action =
|
||||||
return ARProcess
|
return ARProcess
|
||||||
GitReceivePack spec -> do
|
GitReceivePack spec -> do
|
||||||
(sharer, repo, repoPath) <- resolveSpec' repoDir spec
|
(sharer, repo, repoPath) <- resolveSpec' repoDir spec
|
||||||
-- Now we need to check whether the authenticated user (can get its
|
can <- canPushTo sharer repo
|
||||||
-- details with 'askAuthDetails') has write access to the repo.
|
if can
|
||||||
-- 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 whenGitRepoExists repoPath $ do
|
then whenGitRepoExists repoPath $ do
|
||||||
execute "git-receive-pack" [repoPath]
|
execute "git-receive-pack" [repoPath]
|
||||||
return ARProcess
|
return ARProcess
|
||||||
|
|
Loading…
Reference in a new issue