types,appview,knotserver: replace object.Commit with types.Commit #860

merged
opened by oppi.li targeting master from op/rkowwwsvpoqx
Changed files
+39 -136
appview
commitverify
pages
templates
repo
crypto
knotserver
git
xrpc
patchutil
types
+6 -45
appview/commitverify/verify.go
···
import (
"log"
-
"github.com/go-git/go-git/v5/plumbing/object"
"tangled.org/core/appview/db"
"tangled.org/core/appview/models"
"tangled.org/core/crypto"
···
return ""
}
-
func GetVerifiedObjectCommits(e db.Execer, emailToDid map[string]string, commits []*object.Commit) (VerifiedCommits, error) {
-
ndCommits := []types.NiceDiff{}
-
for _, commit := range commits {
-
ndCommits = append(ndCommits, ObjectCommitToNiceDiff(commit))
-
}
-
return GetVerifiedCommits(e, emailToDid, ndCommits)
-
}
-
-
func GetVerifiedCommits(e db.Execer, emailToDid map[string]string, ndCommits []types.NiceDiff) (VerifiedCommits, error) {
vcs := VerifiedCommits{}
didPubkeyCache := make(map[string][]models.PublicKey)
for _, commit := range ndCommits {
-
c := commit.Commit
-
-
committerEmail := c.Committer.Email
if did, exists := emailToDid[committerEmail]; exists {
// check if we've already fetched public keys for this did
pubKeys, ok := didPubkeyCache[did]
···
}
// try to verify with any associated pubkeys
for _, pk := range pubKeys {
-
if _, ok := crypto.VerifyCommitSignature(pk.Key, commit); ok {
fp, err := crypto.SSHFingerprint(pk.Key)
if err != nil {
log.Println("error computing ssh fingerprint:", err)
}
-
vc := verifiedCommit{fingerprint: fp, hash: c.This}
vcs[vc] = struct{}{}
break
}
···
return vcs, nil
}
-
-
// ObjectCommitToNiceDiff is a compatibility function to convert a
-
// commit object into a NiceDiff structure.
-
func ObjectCommitToNiceDiff(c *object.Commit) types.NiceDiff {
-
var niceDiff types.NiceDiff
-
-
// set commit information
-
niceDiff.Commit.Message = c.Message
-
niceDiff.Commit.Author = c.Author
-
niceDiff.Commit.This = c.Hash.String()
-
niceDiff.Commit.Committer = c.Committer
-
niceDiff.Commit.Tree = c.TreeHash.String()
-
niceDiff.Commit.PGPSignature = c.PGPSignature
-
-
changeId, ok := c.ExtraHeaders["change-id"]
-
if ok {
-
niceDiff.Commit.ChangedId = string(changeId)
-
}
-
-
// set parent hash if available
-
if len(c.ParentHashes) > 0 {
-
niceDiff.Commit.Parent = c.ParentHashes[0].String()
-
}
-
-
// XXX: Stats and Diff fields are typically populated
-
// after fetching the actual diff information, which isn't
-
// directly available in the commit object itself.
-
-
return niceDiff
-
}
···
import (
"log"
"tangled.org/core/appview/db"
"tangled.org/core/appview/models"
"tangled.org/core/crypto"
···
return ""
}
+
func GetVerifiedCommits(e db.Execer, emailToDid map[string]string, ndCommits []types.Commit) (VerifiedCommits, error) {
vcs := VerifiedCommits{}
didPubkeyCache := make(map[string][]models.PublicKey)
for _, commit := range ndCommits {
+
committerEmail := commit.Committer.Email
if did, exists := emailToDid[committerEmail]; exists {
// check if we've already fetched public keys for this did
pubKeys, ok := didPubkeyCache[did]
···
}
// try to verify with any associated pubkeys
+
payload := commit.Payload()
+
signature := commit.PGPSignature
for _, pk := range pubKeys {
+
if _, ok := crypto.VerifySignature([]byte(pk.Key), []byte(signature), []byte(payload)); ok {
fp, err := crypto.SSHFingerprint(pk.Key)
if err != nil {
log.Println("error computing ssh fingerprint:", err)
}
+
vc := verifiedCommit{fingerprint: fp, hash: commit.This}
vcs[vc] = struct{}{}
break
}
···
return vcs, nil
}
+1 -2
appview/pages/pages.go
···
"github.com/bluesky-social/indigo/atproto/identity"
"github.com/bluesky-social/indigo/atproto/syntax"
"github.com/go-git/go-git/v5/plumbing"
-
"github.com/go-git/go-git/v5/plumbing/object"
)
//go:embed templates/* static legal
···
RepoInfo repoinfo.RepoInfo
Active string
TagMap map[string][]string
-
CommitsTrunc []*object.Commit
TagsTrunc []*types.TagReference
BranchesTrunc []types.Branch
// ForkInfo *types.ForkInfo
···
"github.com/bluesky-social/indigo/atproto/identity"
"github.com/bluesky-social/indigo/atproto/syntax"
"github.com/go-git/go-git/v5/plumbing"
)
//go:embed templates/* static legal
···
RepoInfo repoinfo.RepoInfo
Active string
TagMap map[string][]string
+
CommitsTrunc []types.Commit
TagsTrunc []*types.TagReference
BranchesTrunc []types.Branch
// ForkInfo *types.ForkInfo
+1 -1
appview/pages/templates/repo/commit.html
···
{{ end }}
<span class="px-1 select-none before:content-['\00B7']"></span>
-
{{ template "repo/fragments/time" $commit.Author.When }}
<span class="px-1 select-none before:content-['\00B7']"></span>
<a href="/{{ $repo }}/commit/{{ $commit.This }}" class="no-underline hover:underline text-gray-500 dark:text-gray-300">{{ slice $commit.This 0 8 }}</a>
···
{{ end }}
<span class="px-1 select-none before:content-['\00B7']"></span>
+
{{ template "repo/fragments/time" $commit.Committer.When }}
<span class="px-1 select-none before:content-['\00B7']"></span>
<a href="/{{ $repo }}/commit/{{ $commit.This }}" class="no-underline hover:underline text-gray-500 dark:text-gray-300">{{ slice $commit.This 0 8 }}</a>
+1 -1
appview/repo/index.go
···
l.Error("failed to get email to did map", "err", err)
}
-
vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, commitsTrunc)
if err != nil {
l.Error("failed to GetVerifiedObjectCommits", "err", err)
}
···
l.Error("failed to get email to did map", "err", err)
}
+
vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, commitsTrunc)
if err != nil {
l.Error("failed to GetVerifiedObjectCommits", "err", err)
}
+2 -2
appview/repo/log.go
···
l.Error("failed to fetch email to did mapping", "err", err)
}
-
vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, xrpcResp.Commits)
if err != nil {
l.Error("failed to GetVerifiedObjectCommits", "err", err)
}
···
l.Error("failed to get email to did mapping", "err", err)
}
-
vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, []types.NiceDiff{*result.Diff})
if err != nil {
l.Error("failed to GetVerifiedCommits", "err", err)
}
···
l.Error("failed to fetch email to did mapping", "err", err)
}
+
vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, xrpcResp.Commits)
if err != nil {
l.Error("failed to GetVerifiedObjectCommits", "err", err)
}
···
l.Error("failed to get email to did mapping", "err", err)
}
+
vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, []types.Commit{result.Diff.Commit})
if err != nil {
l.Error("failed to GetVerifiedCommits", "err", err)
}
+1 -3
appview/repo/repo_util.go
···
"tangled.org/core/appview/db"
"tangled.org/core/appview/models"
"tangled.org/core/types"
-
-
"github.com/go-git/go-git/v5/plumbing/object"
)
func sortFiles(files []types.NiceTree) {
···
})
}
-
func uniqueEmails(commits []*object.Commit) []string {
emails := make(map[string]struct{})
for _, commit := range commits {
if commit.Author.Email != "" {
···
"tangled.org/core/appview/db"
"tangled.org/core/appview/models"
"tangled.org/core/types"
)
func sortFiles(files []types.NiceTree) {
···
})
}
+
func uniqueEmails(commits []types.Commit) []string {
emails := make(map[string]struct{})
for _, commit := range commits {
if commit.Author.Email != "" {
+1 -34
crypto/verify.go
···
"crypto/sha256"
"encoding/base64"
"fmt"
-
"strings"
"github.com/hiddeco/sshsig"
"golang.org/x/crypto/ssh"
-
"tangled.org/core/types"
)
func VerifySignature(pubKey, signature, payload []byte) (error, bool) {
···
// multiple algorithms but sha-512 is most secure, and git's ssh signing defaults
// to sha-512 for all key types anyway.
err = sshsig.Verify(buf, sig, pub, sshsig.HashSHA512, "git")
-
return err, err == nil
-
}
-
-
// VerifyCommitSignature reconstructs the payload used to sign a commit. This is
-
// essentially the git cat-file output but without the gpgsig header.
-
//
-
// Caveats: signature verification will fail on commits with more than one parent,
-
// i.e. merge commits, because types.NiceDiff doesn't carry more than one Parent field
-
// and we are unable to reconstruct the payload correctly.
-
//
-
// Ideally this should directly operate on an *object.Commit.
-
func VerifyCommitSignature(pubKey string, commit types.NiceDiff) (error, bool) {
-
signature := commit.Commit.PGPSignature
-
-
author := bytes.NewBuffer([]byte{})
-
committer := bytes.NewBuffer([]byte{})
-
commit.Commit.Author.Encode(author)
-
commit.Commit.Committer.Encode(committer)
-
-
payload := strings.Builder{}
-
fmt.Fprintf(&payload, "tree %s\n", commit.Commit.Tree)
-
if commit.Commit.Parent != "" {
-
fmt.Fprintf(&payload, "parent %s\n", commit.Commit.Parent)
-
}
-
fmt.Fprintf(&payload, "author %s\n", author.String())
-
fmt.Fprintf(&payload, "committer %s\n", committer.String())
-
if commit.Commit.ChangedId != "" {
-
fmt.Fprintf(&payload, "change-id %s\n", commit.Commit.ChangedId)
-
}
-
fmt.Fprintf(&payload, "\n%s", commit.Commit.Message)
-
-
return VerifySignature([]byte(pubKey), []byte(signature), []byte(payload.String()))
}
// SSHFingerprint computes the fingerprint of the supplied ssh pubkey.
···
"crypto/sha256"
"encoding/base64"
"fmt"
"github.com/hiddeco/sshsig"
"golang.org/x/crypto/ssh"
)
func VerifySignature(pubKey, signature, payload []byte) (error, bool) {
···
// multiple algorithms but sha-512 is most secure, and git's ssh signing defaults
// to sha-512 for all key types anyway.
err = sshsig.Verify(buf, sig, pub, sshsig.HashSHA512, "git")
+
return err, err == nil
}
// SSHFingerprint computes the fingerprint of the supplied ssh pubkey.
+1 -17
knotserver/git/diff.go
···
nd.Diff = append(nd.Diff, ndiff)
}
-
nd.Stat.FilesChanged = len(diffs)
-
nd.Commit.This = c.Hash.String()
-
nd.Commit.PGPSignature = c.PGPSignature
-
nd.Commit.Committer = c.Committer
-
nd.Commit.Tree = c.TreeHash.String()
-
-
if parent.Hash.IsZero() {
-
nd.Commit.Parent = ""
-
} else {
-
nd.Commit.Parent = parent.Hash.String()
-
}
-
nd.Commit.Author = c.Author
-
nd.Commit.Message = c.Message
-
-
if v, ok := c.ExtraHeaders["change-id"]; ok {
-
nd.Commit.ChangedId = string(v)
-
}
return &nd, nil
}
···
nd.Diff = append(nd.Diff, ndiff)
}
+
nd.Commit.FromGoGitCommit(c)
return &nd, nil
}
+6 -1
knotserver/xrpc/repo_log.go
···
return
}
// Create response using existing types.RepoLogResponse
response := types.RepoLogResponse{
-
Commits: commits,
Ref: ref,
Page: (offset / limit) + 1,
PerPage: limit,
···
return
}
+
tcommits := make([]types.Commit, len(commits))
+
for i, c := range commits {
+
tcommits[i].FromGoGitCommit(c)
+
}
+
// Create response using existing types.RepoLogResponse
response := types.RepoLogResponse{
+
Commits: tcommits,
Ref: ref,
Page: (offset / limit) + 1,
PerPage: limit,
-1
patchutil/patchutil.go
···
}
nd := types.NiceDiff{}
-
nd.Commit.Parent = targetBranch
for _, d := range diffs {
ndiff := types.Diff{}
···
}
nd := types.NiceDiff{}
for _, d := range diffs {
ndiff := types.Diff{}
+2 -12
types/diff.go
···
import (
"github.com/bluekeyes/go-gitdiff/gitdiff"
-
"github.com/go-git/go-git/v5/plumbing/object"
)
type DiffOpts struct {
···
// A nicer git diff representation.
type NiceDiff struct {
-
Commit struct {
-
Message string `json:"message"`
-
Author object.Signature `json:"author"`
-
This string `json:"this"`
-
Parent string `json:"parent"`
-
PGPSignature string `json:"pgp_signature"`
-
Committer object.Signature `json:"committer"`
-
Tree string `json:"tree"`
-
ChangedId string `json:"change_id"`
-
} `json:"commit"`
-
Stat struct {
FilesChanged int `json:"files_changed"`
Insertions int `json:"insertions"`
Deletions int `json:"deletions"`
···
import (
"github.com/bluekeyes/go-gitdiff/gitdiff"
)
type DiffOpts struct {
···
// A nicer git diff representation.
type NiceDiff struct {
+
Commit Commit `json:"commit"`
+
Stat struct {
FilesChanged int `json:"files_changed"`
Insertions int `json:"insertions"`
Deletions int `json:"deletions"`
+17 -17
types/repo.go
···
)
type RepoIndexResponse struct {
-
IsEmpty bool `json:"is_empty"`
-
Ref string `json:"ref,omitempty"`
-
Readme string `json:"readme,omitempty"`
-
ReadmeFileName string `json:"readme_file_name,omitempty"`
-
Commits []*object.Commit `json:"commits,omitempty"`
-
Description string `json:"description,omitempty"`
-
Files []NiceTree `json:"files,omitempty"`
-
Branches []Branch `json:"branches,omitempty"`
-
Tags []*TagReference `json:"tags,omitempty"`
-
TotalCommits int `json:"total_commits,omitempty"`
}
type RepoLogResponse struct {
-
Commits []*object.Commit `json:"commits,omitempty"`
-
Ref string `json:"ref,omitempty"`
-
Description string `json:"description,omitempty"`
-
Log bool `json:"log,omitempty"`
-
Total int `json:"total,omitempty"`
-
Page int `json:"page,omitempty"`
-
PerPage int `json:"per_page,omitempty"`
}
type RepoCommitResponse struct {
···
)
type RepoIndexResponse struct {
+
IsEmpty bool `json:"is_empty"`
+
Ref string `json:"ref,omitempty"`
+
Readme string `json:"readme,omitempty"`
+
ReadmeFileName string `json:"readme_file_name,omitempty"`
+
Commits []Commit `json:"commits,omitempty"`
+
Description string `json:"description,omitempty"`
+
Files []NiceTree `json:"files,omitempty"`
+
Branches []Branch `json:"branches,omitempty"`
+
Tags []*TagReference `json:"tags,omitempty"`
+
TotalCommits int `json:"total_commits,omitempty"`
}
type RepoLogResponse struct {
+
Commits []Commit `json:"commits,omitempty"`
+
Ref string `json:"ref,omitempty"`
+
Description string `json:"description,omitempty"`
+
Log bool `json:"log,omitempty"`
+
Total int `json:"total,omitempty"`
+
Page int `json:"page,omitempty"`
+
PerPage int `json:"per_page,omitempty"`
}
type RepoCommitResponse struct {