@ -1,8 +1,13 @@
package dehub
package dehub
import (
import (
"errors"
"regexp"
"testing"
"testing"
"dehub.dev/src/dehub.git/accessctl"
"dehub.dev/src/dehub.git/sigcred"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing"
)
)
@ -14,23 +19,23 @@ func TestConfigChange(t *testing.T) {
// commit the initial staged changes, which merely include the config and
// commit the initial staged changes, which merely include the config and
// public key
// public key
gitCommit := h . assertCommitChange ( true , "commit configuration" , rootSig )
gitCommit := h . assertCommitChange ( verifyShouldSucceed , "commit configuration" , rootSig )
gitCommits = append ( gitCommits , gitCommit )
gitCommits = append ( gitCommits , gitCommit )
// create a new account and add it to the configuration. That commit should
// create a new account and add it to the configuration. That commit should
// not be verifiable, though
// not be verifiable, though
tootSig := h . stageNewAccount ( "toot" , false )
tootSig := h . stageNewAccount ( "toot" , false )
h . stageCfg ( )
h . stageCfg ( )
h . assertCommitChange ( false , "add toot user" , tootSig )
h . assertCommitChange ( verifyShouldFail , "add toot user" , tootSig )
// now add with the root user, this should work.
// now add with the root user, this should work.
h . stageCfg ( )
h . stageCfg ( )
gitCommit = h . assertCommitChange ( true , "add toot user" , rootSig )
gitCommit = h . assertCommitChange ( verifyShouldSucceed , "add toot user" , rootSig )
gitCommits = append ( gitCommits , gitCommit )
gitCommits = append ( gitCommits , gitCommit )
// _now_ the toot user should be able to do things.
// _now_ the toot user should be able to do things.
h . stage ( map [ string ] string { "foo/bar" : "what a cool file" } )
h . stage ( map [ string ] string { "foo/bar" : "what a cool file" } )
gitCommit = h . assertCommitChange ( true , "add a cool file" , tootSig )
gitCommit = h . assertCommitChange ( verifyShouldSucceed , "add a cool file" , tootSig )
gitCommits = append ( gitCommits , gitCommit )
gitCommits = append ( gitCommits , gitCommit )
if err := h . repo . VerifyCommits ( MainRefName , gitCommits ) ; err != nil {
if err := h . repo . VerifyCommits ( MainRefName , gitCommits ) ; err != nil {
@ -47,14 +52,13 @@ func TestMainAncestryRequirement(t *testing.T) {
// stage and try to add to the "other" branch, it shouldn't work though
// stage and try to add to the "other" branch, it shouldn't work though
h . stageCfg ( )
h . stageCfg ( )
h . assertCommitChange ( false , "starting new branch at other" , rootSig )
h . assertCommitChange ( verifyShouldFail , "starting new branch at other" , rootSig )
} )
} )
t . Run ( "new branch, single commit" , func ( t * testing . T ) {
t . Run ( "new branch, single commit" , func ( t * testing . T ) {
h := newHarness ( t )
h := newHarness ( t )
rootSig := h . stageNewAccount ( "root" , false )
rootSig := h . stageNewAccount ( "root" , false )
h . stageCfg ( )
h . assertCommitChange ( verifyShouldSucceed , "add cfg" , rootSig )
h . assertCommitChange ( true , "add cfg" , rootSig )
// set HEAD to this other branch which doesn't really exist
// set HEAD to this other branch which doesn't really exist
ref := plumbing . NewSymbolicReference ( plumbing . HEAD , otherBranch )
ref := plumbing . NewSymbolicReference ( plumbing . HEAD , otherBranch )
@ -63,7 +67,7 @@ func TestMainAncestryRequirement(t *testing.T) {
}
}
h . stageCfg ( )
h . stageCfg ( )
h . assertCommitChange ( false , "starting new branch at other" , rootSig )
h . assertCommitChange ( verifyShouldFail , "starting new branch at other" , rootSig )
} )
} )
}
}
@ -77,5 +81,354 @@ func TestAnonymousCommits(t *testing.T) {
- type : signature
- type : signature
any : true
any : true
` )
` )
h . assertCommitChange ( true , "this will work" , anonSig )
h . assertCommitChange ( verifyShouldSucceed , "this will work" , anonSig )
}
func TestNonFastForwardCommits ( t * testing . T ) {
h := newHarness ( t )
rootSig := h . stageNewAccount ( "root" , false )
initCommit := h . assertCommitChange ( verifyShouldSucceed , "init" , rootSig )
// add another commit
h . stage ( map [ string ] string { "foo" : "foo" } )
fooCommit := h . assertCommitChange ( verifyShouldSucceed , "foo" , rootSig )
commitOn := func ( hash plumbing . Hash , msg string ) GitCommit {
ref := plumbing . NewHashReference ( plumbing . HEAD , hash )
if err := h . repo . GitRepo . Storer . SetReference ( ref ) ; err != nil {
h . t . Fatal ( err )
} else if commitChange , err := h . repo . NewCommitChange ( "bar" ) ; err != nil {
h . t . Fatal ( err )
} else if commitChange , err = h . repo . AccreditCommit ( commitChange , rootSig ) ; err != nil {
h . t . Fatal ( err )
} else if gitCommit , err := h . repo . Commit ( commitChange ) ; err != nil {
h . t . Fatal ( err )
} else {
return gitCommit
}
panic ( "can't get here" )
}
// checkout initCommit directly, make a new commit on top of it, and try to
// verify that (this is too fancy for the harness, must be done manually).
h . stage ( map [ string ] string { "bar" : "bar" } )
barCommit := commitOn ( initCommit . GitCommit . Hash , "bar" )
err := h . repo . VerifyCommits ( MainRefName , [ ] GitCommit { barCommit } )
if ! errors . As ( err , new ( accessctl . ErrCommitRequestDenied ) ) {
h . t . Fatalf ( "expected ErrCommitRequestDenied, got: %v" , err )
}
// check main back out (fooCommit should be checked out), and modify the
// config to allow nonFF commits, and add another bogus commit on top.
h . checkout ( MainRefName )
h . stageAccessControls ( `
- action : allow
filters :
- type : commit_attributes
non_fast_forward : true ` )
h . stageCfg ( )
allowNonFFCommit := h . assertCommitChange ( verifyShouldSucceed , "allow non-ff" , rootSig )
h . stage ( map [ string ] string { "foo" : "foo foo" } )
h . assertCommitChange ( verifyShouldSucceed , "foo foo" , rootSig )
// checking out allowNonFFCommit directly and performing a nonFF commit
// should work now.
h . stage ( map [ string ] string { "baz" : "baz" } )
bazCommit := commitOn ( allowNonFFCommit . GitCommit . Hash , "baz" )
if err = h . repo . VerifyCommits ( MainRefName , [ ] GitCommit { bazCommit } ) ; err != nil {
h . t . Fatal ( err )
}
// verifying the full history should also work
gitCommits := [ ] GitCommit { initCommit , fooCommit , allowNonFFCommit , bazCommit }
if err = h . repo . VerifyCommits ( MainRefName , gitCommits ) ; err != nil {
h . t . Fatal ( err )
}
}
func TestCanSetBranchHEADTo ( t * testing . T ) {
type toTest struct {
// branchName and hash are the arguments passed into
// VerifyCanSetBranchHEADTo.
branchName plumbing . ReferenceName
hash plumbing . Hash
// if set then the branch will have its HEAD reset to this hash prior to
// calling VerifyCanSetBranchHEADTo.
resetTo plumbing . Hash
}
type test struct {
descr string
init func ( h * harness , rootSig sigcred . SignifierInterface ) toTest
// If true then the verify call is expected to fail. The string is a
// regex which should match the unwrapped error returned.
expErr string
}
tests := [ ] test {
{
descr : "creation of main" ,
init : func ( h * harness , rootSig sigcred . SignifierInterface ) toTest {
// checkout other and build on top of that, so that when
// VerifyCanSetBranchHEADTo is called main won't exist.
other := plumbing . NewBranchReferenceName ( "other" )
h . checkout ( other )
initCommit := h . assertCommitChange ( verifySkip , "init" , rootSig )
return toTest {
branchName : MainRefName ,
hash : initCommit . GitCommit . Hash ,
}
} ,
} ,
{
descr : "main ff" ,
init : func ( h * harness , rootSig sigcred . SignifierInterface ) toTest {
initCommit := h . assertCommitChange ( verifySkip , "init" , rootSig )
h . stage ( map [ string ] string { "foo" : "foo" } )
nextCommit := h . assertCommitChange ( verifySkip , "next" , rootSig )
return toTest {
branchName : MainRefName ,
hash : nextCommit . GitCommit . Hash ,
resetTo : initCommit . GitCommit . Hash ,
}
} ,
} ,
{
descr : "new branch, no main" ,
init : func ( h * harness , rootSig sigcred . SignifierInterface ) toTest {
// checkout other and build on top of that, so that when
// VerifyCanSetBranchHEADTo is called main won't exist.
other := plumbing . NewBranchReferenceName ( "other" )
h . checkout ( other )
initCommit := h . assertCommitChange ( verifySkip , "init" , rootSig )
return toTest {
branchName : plumbing . NewBranchReferenceName ( "other2" ) ,
hash : initCommit . GitCommit . Hash ,
}
} ,
expErr : ` ^cannot verify commits in branch "refs/heads/other2" when no main branch exists$ ` ,
} ,
{
// this case isn't generally possible, unless someone manually
// creates a branch in an empty repo on the remote
descr : "existing branch, no main" ,
init : func ( h * harness , rootSig sigcred . SignifierInterface ) toTest {
// checkout other and build on top of that, so that when
// VerifyCanSetBranchHEADTo is called main won't exist.
other := plumbing . NewBranchReferenceName ( "other" )
h . checkout ( other )
initCommit := h . assertCommitChange ( verifySkip , "init" , rootSig )
h . stage ( map [ string ] string { "foo" : "foo" } )
fooCommit := h . assertCommitChange ( verifySkip , "foo" , rootSig )
return toTest {
branchName : other ,
hash : fooCommit . GitCommit . Hash ,
resetTo : initCommit . GitCommit . Hash ,
}
} ,
expErr : ` ^cannot verify commits in branch "refs/heads/other" when no main branch exists$ ` ,
} ,
{
descr : "new branch, not ancestor of main" ,
init : func ( h * harness , rootSig sigcred . SignifierInterface ) toTest {
h . assertCommitChange ( verifySkip , "init" , rootSig )
// create new branch with no HEAD, and commit on that.
other := plumbing . NewBranchReferenceName ( "other" )
ref := plumbing . NewSymbolicReference ( plumbing . HEAD , other )
if err := h . repo . GitRepo . Storer . SetReference ( ref ) ; err != nil {
t . Fatal ( err )
}
h . stageCfg ( )
h . stage ( map [ string ] string { "foo" : "foo" } )
badInitCommit := h . assertCommitChange ( verifySkip , "a different init" , rootSig )
return toTest {
branchName : plumbing . NewBranchReferenceName ( "other2" ) ,
hash : badInitCommit . GitCommit . Hash ,
}
} ,
expErr : ` ^commit "[0-9a-f]+" must be direct descendant of root commit of "main" \("[0-9a-f]+"\)$ ` ,
} ,
{
// this case isn't generally possible, unless someone manually
// creates a branch in an empty repo on the remote
descr : "existing branch, not ancestor of main" ,
init : func ( h * harness , rootSig sigcred . SignifierInterface ) toTest {
h . assertCommitChange ( verifySkip , "init" , rootSig )
// create new branch with no HEAD, and commit on that.
other := plumbing . NewBranchReferenceName ( "other" )
ref := plumbing . NewSymbolicReference ( plumbing . HEAD , other )
if err := h . repo . GitRepo . Storer . SetReference ( ref ) ; err != nil {
t . Fatal ( err )
}
h . stageCfg ( )
h . stage ( map [ string ] string { "foo" : "foo" } )
badInitCommit := h . assertCommitChange ( verifySkip , "a different init" , rootSig )
h . stage ( map [ string ] string { "bar" : "bar" } )
barCommit := h . assertCommitChange ( verifySkip , "bar" , rootSig )
return toTest {
branchName : other ,
hash : barCommit . GitCommit . Hash ,
resetTo : badInitCommit . GitCommit . Hash ,
}
} ,
expErr : ` ^commit "[0-9a-f]+" must be direct descendant of root commit of "main" \("[0-9a-f]+"\)$ ` ,
} ,
{
descr : "new branch off of main" ,
init : func ( h * harness , rootSig sigcred . SignifierInterface ) toTest {
initCommit := h . assertCommitChange ( verifySkip , "init" , rootSig )
other := plumbing . NewBranchReferenceName ( "other" )
h . checkout ( other )
h . stage ( map [ string ] string { "foo" : "foo" } )
fooCommit := h . assertCommitChange ( verifySkip , "foo" , rootSig )
return toTest {
branchName : other ,
hash : fooCommit . GitCommit . Hash ,
resetTo : initCommit . GitCommit . Hash ,
}
} ,
} ,
{
descr : "new branch off of older main commit" ,
init : func ( h * harness , rootSig sigcred . SignifierInterface ) toTest {
initCommit := h . assertCommitChange ( verifySkip , "init" , rootSig )
h . stage ( map [ string ] string { "foo" : "foo" } )
h . assertCommitChange ( verifySkip , "foo" , rootSig )
other := plumbing . NewBranchReferenceName ( "other" )
h . checkout ( other )
h . reset ( initCommit . GitCommit . Hash , git . HardReset )
h . stage ( map [ string ] string { "bar" : "bar" } )
barCommit := h . assertCommitChange ( verifySkip , "bar" , rootSig )
return toTest {
branchName : other ,
hash : barCommit . GitCommit . Hash ,
resetTo : initCommit . GitCommit . Hash ,
}
} ,
} ,
{
descr : "branch ff" ,
init : func ( h * harness , rootSig sigcred . SignifierInterface ) toTest {
h . assertCommitChange ( verifySkip , "init" , rootSig )
other := plumbing . NewBranchReferenceName ( "other" )
h . checkout ( other )
var commits [ ] GitCommit
for _ , str := range [ ] string { "foo" , "bar" , "baz" , "biz" , "buz" } {
h . stage ( map [ string ] string { str : str } )
commit := h . assertCommitChange ( verifySkip , str , rootSig )
commits = append ( commits , commit )
}
return toTest {
branchName : other ,
hash : commits [ len ( commits ) - 1 ] . GitCommit . Hash ,
resetTo : commits [ 0 ] . GitCommit . Hash ,
}
} ,
} ,
{
descr : "main nonff" ,
init : func ( h * harness , rootSig sigcred . SignifierInterface ) toTest {
initCommit := h . assertCommitChange ( verifySkip , "init" , rootSig )
h . stage ( map [ string ] string { "foo" : "foo" } )
h . assertCommitChange ( verifySkip , "foo" , rootSig )
// start another branch back at init and make a new commit on it
other := plumbing . NewBranchReferenceName ( "other" )
h . checkout ( other )
h . reset ( initCommit . GitCommit . Hash , git . HardReset )
h . stage ( map [ string ] string { "bar" : "bar" } )
barCommit := h . assertCommitChange ( verifySkip , "bar" , rootSig )
return toTest {
branchName : MainRefName ,
hash : barCommit . GitCommit . Hash ,
}
} ,
expErr : ` ^commit matched and denied by this access control: ` ,
} ,
{
descr : "branch nonff" ,
init : func ( h * harness , rootSig sigcred . SignifierInterface ) toTest {
h . assertCommitChange ( verifySkip , "init" , rootSig )
other := plumbing . NewBranchReferenceName ( "other" )
h . checkout ( other )
h . stage ( map [ string ] string { "foo" : "foo" } )
fooCommit := h . assertCommitChange ( verifySkip , "foo" , rootSig )
h . stage ( map [ string ] string { "bar" : "bar" } )
h . assertCommitChange ( verifySkip , "bar" , rootSig )
other2 := plumbing . NewBranchReferenceName ( "other2" )
h . checkout ( other2 )
h . reset ( fooCommit . GitCommit . Hash , git . HardReset )
h . stage ( map [ string ] string { "baz" : "baz" } )
bazCommit := h . assertCommitChange ( verifySkip , "baz" , rootSig )
return toTest {
branchName : other ,
hash : bazCommit . GitCommit . Hash ,
}
} ,
} ,
}
for _ , test := range tests {
t . Run ( test . descr , func ( t * testing . T ) {
h := newHarness ( t )
rootSig := h . stageNewAccount ( "root" , false )
toTest := test . init ( h , rootSig )
if toTest . resetTo != plumbing . ZeroHash {
ref := plumbing . NewHashReference ( toTest . branchName , toTest . resetTo )
if err := h . repo . GitRepo . Storer . SetReference ( ref ) ; err != nil {
t . Fatal ( err )
}
}
err := h . repo . VerifyCanSetBranchHEADTo ( toTest . branchName , toTest . hash )
if test . expErr == "" {
if err != nil {
t . Fatalf ( "unexpected error: %v" , err )
}
return
} else if err == nil {
t . Fatal ( "expected verification to fail" )
}
ogErr := err
for {
if unwrappedErr := errors . Unwrap ( err ) ; unwrappedErr != nil {
err = unwrappedErr
} else {
break
}
}
errRegex := regexp . MustCompile ( test . expErr )
if ! errRegex . MatchString ( err . Error ( ) ) {
t . Fatalf ( "\nexpected error of form %q\nbut got: %v" , test . expErr , ogErr )
}
} )
}
}
}