aff3daab19
--- type: change message: |- Modify how SignifierInterface is produced so it always sets AccountID on Credentials Previously it was the responsibility of the caller of the Sign method to set the AccountID on the produced Credential, but this didn't really make sense. This commit makes it so that all SignifierInterface's produced by Signifier implicitly set the AccountID field. The solution here is still a bit hacky, and ultimately the real solution will probably be to refactor the structore of Credential, so that it doesn't have AccountID. change_hash: ADPuz04GuyxWwjo/0/jc7DcsPMl5rK0osSpaqmUxv818 credentials: - type: pgp_signature pub_key_id: 95C46FA6A41148AC body: iQIzBAABAgAdFiEEJ6tQKp6olvZKJ0lwlcRvpqQRSKwFAl5r+hgACgkQlcRvpqQRSKzwYBAAsY4tj+E5xtJSZ1TvrS0mwJ/lSHYWE4rS3eDMY3JUJLE1tr5k3OTRtUhh2UHCsArXSVF4sU8cBSCtf2noaThQm8KQghPMgoZ1LnPd4BnxxlE2gPik4FMcv+mCv9OgUh0AUO+rSXeYJA3oWunaW9kYollUdX/mVTQTmmbLBqBpeXF/TQO/bJTEEzA853j5QDT8//onfSIlzUw0UB57IZZZImp5/XrggHBbKdfhUTJ75LGMgDEDvDNIdV8lBys+RnMzK0Yj6EvLQhsw426+0Sf9vX3jtzj6WKhmi8QyYvcxIbcrWUScEfA/RAgf0A8KhqKq91bicSHjvyK1TZRSSWcS43ewamgvVWx0KSYYoIn7PPwOTmpHP8u6RzGEQFjOhP1EaGytQJKMXidU6CPTh+pYVtPZc8oLAwk+DyMquqfUSbzN/63t90HpTm7uycuzOnQxilYe2HKlbMJCId0a0DyAFrA+0pNRz0tyd3DvF4svCdEy82rzlUGEhq7aIJKoXIut+fKGEBd6Znz6oX15CyQq0oPthZcCqgFR0oTqufvV2iWo+26cd9dVTPVbJA9kSbaFchgdAqCkPA5wDVuNJJtMftf7STW8Lm6dnU6q9YFjZVdR55WtvUCINxBUtOirRzG1jcS0VNhhtb+SMNATEvDGJmt6neHM6Z17MAdwGS+s/hA= account: mediocregopher
250 lines
6.2 KiB
Go
250 lines
6.2 KiB
Go
package dehub
|
|
|
|
import (
|
|
"bytes"
|
|
"dehub/accessctl"
|
|
"dehub/sigcred"
|
|
"io"
|
|
"math/rand"
|
|
"path/filepath"
|
|
"runtime/debug"
|
|
"testing"
|
|
|
|
"gopkg.in/src-d/go-git.v4"
|
|
"gopkg.in/src-d/go-git.v4/plumbing"
|
|
yaml "gopkg.in/yaml.v2"
|
|
)
|
|
|
|
type harness struct {
|
|
t *testing.T
|
|
rand *rand.Rand
|
|
repo *Repo
|
|
cfg *Config
|
|
sig sigcred.SignifierInterface
|
|
}
|
|
|
|
func newHarness(t *testing.T) *harness {
|
|
rand := rand.New(rand.NewSource(0xb4eadb01))
|
|
sig, pubKeyBody := sigcred.SignifierPGPTmp("root", rand)
|
|
pubKeyPath := filepath.Join(DehubDir, "root.asc")
|
|
|
|
cfg := &Config{
|
|
Accounts: []Account{{
|
|
ID: "root",
|
|
Signifiers: []sigcred.Signifier{{PGPPublicKeyFile: &sigcred.SignifierPGPFile{
|
|
Path: pubKeyPath,
|
|
}}},
|
|
}},
|
|
AccessControls: []accessctl.BranchAccessControl{
|
|
{
|
|
BranchPattern: "**",
|
|
ChangeAccessControls: []accessctl.ChangeAccessControl{
|
|
{
|
|
FilePathPattern: "**",
|
|
Condition: accessctl.Condition{
|
|
Signature: &accessctl.ConditionSignature{
|
|
AccountIDs: []string{"root"},
|
|
Count: "100%",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
cfgBody, err := yaml.Marshal(cfg)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
h := &harness{
|
|
t: t,
|
|
rand: rand,
|
|
repo: InitMemRepo(),
|
|
cfg: cfg,
|
|
sig: sig,
|
|
}
|
|
h.stage(map[string]string{
|
|
ConfigPath: string(cfgBody),
|
|
pubKeyPath: string(pubKeyBody),
|
|
})
|
|
|
|
return h
|
|
}
|
|
|
|
func (h *harness) stage(tree map[string]string) {
|
|
w, err := h.repo.GitRepo.Worktree()
|
|
if err != nil {
|
|
h.t.Fatal(err)
|
|
}
|
|
fs := w.Filesystem
|
|
for path, content := range tree {
|
|
if content == "" {
|
|
if _, err := w.Remove(path); err != nil {
|
|
h.t.Fatalf("error removing %q: %v", path, err)
|
|
}
|
|
continue
|
|
}
|
|
|
|
dir := filepath.Dir(path)
|
|
if err := fs.MkdirAll(dir, 0666); err != nil {
|
|
h.t.Fatalf("error making directory %q: %v", dir, err)
|
|
}
|
|
|
|
f, err := fs.Create(path)
|
|
if err != nil {
|
|
h.t.Fatalf("error creating file %q: %v", path, err)
|
|
|
|
} else if _, err := io.Copy(f, bytes.NewBufferString(content)); err != nil {
|
|
h.t.Fatalf("error writing to file %q: %v", path, err)
|
|
|
|
} else if err := f.Close(); err != nil {
|
|
h.t.Fatalf("error closing file %q: %v", path, err)
|
|
|
|
} else if _, err := w.Add(path); err != nil {
|
|
h.t.Fatalf("error adding file %q to index: %v", path, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (h *harness) changeCommit(msg, accountID string, sig sigcred.SignifierInterface) (Commit, plumbing.Hash) {
|
|
commit, err := h.repo.NewCommitChange(msg)
|
|
if err != nil {
|
|
h.t.Fatalf("failed to create CommitChange: %v", err)
|
|
}
|
|
|
|
if sig != nil {
|
|
if commit, err = h.repo.AccreditCommit(commit, sig); err != nil {
|
|
h.t.Fatalf("failed to accredit commit: %v", err)
|
|
}
|
|
}
|
|
|
|
hash, err := h.repo.Commit(commit, accountID)
|
|
if err != nil {
|
|
h.t.Fatalf("failed to commit ChangeCommit: %v", err)
|
|
}
|
|
|
|
return commit, hash
|
|
}
|
|
|
|
func (h *harness) reset(to plumbing.Hash, mode git.ResetMode) {
|
|
w, err := h.repo.GitRepo.Worktree()
|
|
if err != nil {
|
|
h.t.Fatal(err)
|
|
}
|
|
|
|
err = w.Reset(&git.ResetOptions{
|
|
Commit: to,
|
|
Mode: mode,
|
|
})
|
|
if err != nil {
|
|
h.t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestHasStagedChanges(t *testing.T) {
|
|
harness := newHarness(t)
|
|
assertHasStaged := func(expHasStaged bool) {
|
|
hasStaged, err := harness.repo.HasStagedChanges()
|
|
if err != nil {
|
|
debug.PrintStack()
|
|
t.Fatalf("error calling HasStagedChanges: %v", err)
|
|
} else if hasStaged != expHasStaged {
|
|
debug.PrintStack()
|
|
t.Fatalf("expected HasStagedChanges to return %v", expHasStaged)
|
|
}
|
|
}
|
|
|
|
// the harness starts with some staged changes
|
|
assertHasStaged(true)
|
|
|
|
harness.stage(map[string]string{"foo": "bar"})
|
|
assertHasStaged(true)
|
|
harness.changeCommit("first commit", "root", harness.sig)
|
|
assertHasStaged(false)
|
|
|
|
harness.stage(map[string]string{"foo": ""}) // delete foo
|
|
assertHasStaged(true)
|
|
harness.changeCommit("second commit", "root", harness.sig)
|
|
assertHasStaged(false)
|
|
}
|
|
|
|
// TestOldConfig tests that having an older, now malformed, Config doesn't mess
|
|
// with the current parsing, as long as the default access controls still work.
|
|
func TestOldConfig(t *testing.T) {
|
|
harness := newHarness(t)
|
|
|
|
// overwrite the currently staged config file with an older form
|
|
harness.stage(map[string]string{ConfigPath: `
|
|
---
|
|
accounts:
|
|
- id: root
|
|
signifiers:
|
|
- type: pgp_public_key_file
|
|
path: ".dehub/root.asc"
|
|
|
|
access_controls:
|
|
- pattern: "**"
|
|
condition:
|
|
type: signature
|
|
account_ids:
|
|
- root
|
|
count: 0
|
|
`})
|
|
_, hash0 := harness.changeCommit("first commit, this is going great", "root", harness.sig)
|
|
|
|
// even though that access_controls doesn't actually require any signatures,
|
|
// it should be used because it's not well formed.
|
|
harness.stage(map[string]string{"foo": "no rules!"})
|
|
_, hash1 := harness.changeCommit("ain't no laws", "toot", nil)
|
|
|
|
// verifying the first should work, but not the second.
|
|
if err := harness.repo.VerifyCommit(MainRefName, hash0); err != nil {
|
|
t.Fatalf("first commit %q should be verifiable, but got: %v", hash0, err)
|
|
} else if err := harness.repo.VerifyCommit(MainRefName, hash1); err == nil {
|
|
t.Fatalf("second commit %q should not have been verified", hash1)
|
|
}
|
|
|
|
// reset back to hash0
|
|
harness.reset(hash0, git.HardReset)
|
|
|
|
// make a commit fixing the config. everything should still be fine.
|
|
harness.stage(map[string]string{ConfigPath: `
|
|
---
|
|
accounts:
|
|
- id: root
|
|
signifiers:
|
|
- type: pgp_public_key_file
|
|
path: ".dehub/root.asc"
|
|
`})
|
|
_, hash2 := harness.changeCommit("Fix the config!", "root", harness.sig)
|
|
if err := harness.repo.VerifyCommit(MainRefName, hash2); err != nil {
|
|
t.Fatalf("config fix commit %q should be verifiable, but got: %v", hash2, err)
|
|
}
|
|
}
|
|
|
|
// TestThisRepoStillVerifies opens this actual repository and ensures that all
|
|
// commits in it still verify, given this codebase.
|
|
func TestThisRepoStillVerifies(t *testing.T) {
|
|
repo, err := OpenRepo(".")
|
|
if err != nil {
|
|
t.Fatalf("error opening repo: %v", err)
|
|
}
|
|
|
|
headCommit, _, err := repo.head()
|
|
if err != nil {
|
|
t.Fatalf("error getting repo head: %v", err)
|
|
}
|
|
|
|
checkedOutBranch, err := repo.CheckedOutBranch()
|
|
if err != nil {
|
|
t.Fatalf("error determining checked out branch: %v", err)
|
|
}
|
|
|
|
for _, hash := range headCommit.ParentHashes {
|
|
if err := repo.VerifyCommit(checkedOutBranch, hash); err != nil {
|
|
t.Fatalf("error verifying commit %q of branch %q: %v", hash, checkedOutBranch, err)
|
|
}
|
|
}
|
|
}
|