Monorepo for Tangled tangled.org

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

Signed-off-by: oppiliappan <me@oppi.li>

authored by oppi.li and committed by Tangled b26a5eca da28eba8

Changed files
+39 -136
appview
commitverify
pages
templates
repo
crypto
knotserver
git
xrpc
patchutil
types
+6 -45
appview/commitverify/verify.go
··· 3 import ( 4 "log" 5 6 - "github.com/go-git/go-git/v5/plumbing/object" 7 "tangled.org/core/appview/db" 8 "tangled.org/core/appview/models" 9 "tangled.org/core/crypto" ··· 35 return "" 36 } 37 38 - func GetVerifiedObjectCommits(e db.Execer, emailToDid map[string]string, commits []*object.Commit) (VerifiedCommits, error) { 39 - ndCommits := []types.NiceDiff{} 40 - for _, commit := range commits { 41 - ndCommits = append(ndCommits, ObjectCommitToNiceDiff(commit)) 42 - } 43 - return GetVerifiedCommits(e, emailToDid, ndCommits) 44 - } 45 - 46 - func GetVerifiedCommits(e db.Execer, emailToDid map[string]string, ndCommits []types.NiceDiff) (VerifiedCommits, error) { 47 vcs := VerifiedCommits{} 48 49 didPubkeyCache := make(map[string][]models.PublicKey) 50 51 for _, commit := range ndCommits { 52 - c := commit.Commit 53 - 54 - committerEmail := c.Committer.Email 55 if did, exists := emailToDid[committerEmail]; exists { 56 // check if we've already fetched public keys for this did 57 pubKeys, ok := didPubkeyCache[did] ··· 67 } 68 69 // try to verify with any associated pubkeys 70 for _, pk := range pubKeys { 71 - if _, ok := crypto.VerifyCommitSignature(pk.Key, commit); ok { 72 73 fp, err := crypto.SSHFingerprint(pk.Key) 74 if err != nil { 75 log.Println("error computing ssh fingerprint:", err) 76 } 77 78 - vc := verifiedCommit{fingerprint: fp, hash: c.This} 79 vcs[vc] = struct{}{} 80 break 81 } ··· 86 87 return vcs, nil 88 } 89 - 90 - // ObjectCommitToNiceDiff is a compatibility function to convert a 91 - // commit object into a NiceDiff structure. 92 - func ObjectCommitToNiceDiff(c *object.Commit) types.NiceDiff { 93 - var niceDiff types.NiceDiff 94 - 95 - // set commit information 96 - niceDiff.Commit.Message = c.Message 97 - niceDiff.Commit.Author = c.Author 98 - niceDiff.Commit.This = c.Hash.String() 99 - niceDiff.Commit.Committer = c.Committer 100 - niceDiff.Commit.Tree = c.TreeHash.String() 101 - niceDiff.Commit.PGPSignature = c.PGPSignature 102 - 103 - changeId, ok := c.ExtraHeaders["change-id"] 104 - if ok { 105 - niceDiff.Commit.ChangedId = string(changeId) 106 - } 107 - 108 - // set parent hash if available 109 - if len(c.ParentHashes) > 0 { 110 - niceDiff.Commit.Parent = c.ParentHashes[0].String() 111 - } 112 - 113 - // XXX: Stats and Diff fields are typically populated 114 - // after fetching the actual diff information, which isn't 115 - // directly available in the commit object itself. 116 - 117 - return niceDiff 118 - }
··· 3 import ( 4 "log" 5 6 "tangled.org/core/appview/db" 7 "tangled.org/core/appview/models" 8 "tangled.org/core/crypto" ··· 34 return "" 35 } 36 37 + func GetVerifiedCommits(e db.Execer, emailToDid map[string]string, ndCommits []types.Commit) (VerifiedCommits, error) { 38 vcs := VerifiedCommits{} 39 40 didPubkeyCache := make(map[string][]models.PublicKey) 41 42 for _, commit := range ndCommits { 43 + committerEmail := commit.Committer.Email 44 if did, exists := emailToDid[committerEmail]; exists { 45 // check if we've already fetched public keys for this did 46 pubKeys, ok := didPubkeyCache[did] ··· 56 } 57 58 // try to verify with any associated pubkeys 59 + payload := commit.Payload() 60 + signature := commit.PGPSignature 61 for _, pk := range pubKeys { 62 + if _, ok := crypto.VerifySignature([]byte(pk.Key), []byte(signature), []byte(payload)); ok { 63 64 fp, err := crypto.SSHFingerprint(pk.Key) 65 if err != nil { 66 log.Println("error computing ssh fingerprint:", err) 67 } 68 69 + vc := verifiedCommit{fingerprint: fp, hash: commit.This} 70 vcs[vc] = struct{}{} 71 break 72 } ··· 77 78 return vcs, nil 79 }
+1 -2
appview/pages/pages.go
··· 31 "github.com/bluesky-social/indigo/atproto/identity" 32 "github.com/bluesky-social/indigo/atproto/syntax" 33 "github.com/go-git/go-git/v5/plumbing" 34 - "github.com/go-git/go-git/v5/plumbing/object" 35 ) 36 37 //go:embed templates/* static legal ··· 649 RepoInfo repoinfo.RepoInfo 650 Active string 651 TagMap map[string][]string 652 - CommitsTrunc []*object.Commit 653 TagsTrunc []*types.TagReference 654 BranchesTrunc []types.Branch 655 // ForkInfo *types.ForkInfo
··· 31 "github.com/bluesky-social/indigo/atproto/identity" 32 "github.com/bluesky-social/indigo/atproto/syntax" 33 "github.com/go-git/go-git/v5/plumbing" 34 ) 35 36 //go:embed templates/* static legal ··· 648 RepoInfo repoinfo.RepoInfo 649 Active string 650 TagMap map[string][]string 651 + CommitsTrunc []types.Commit 652 TagsTrunc []*types.TagReference 653 BranchesTrunc []types.Branch 654 // ForkInfo *types.ForkInfo
+1 -1
appview/pages/templates/repo/commit.html
··· 35 {{ end }} 36 37 <span class="px-1 select-none before:content-['\00B7']"></span> 38 - {{ template "repo/fragments/time" $commit.Author.When }} 39 <span class="px-1 select-none before:content-['\00B7']"></span> 40 41 <a href="/{{ $repo }}/commit/{{ $commit.This }}" class="no-underline hover:underline text-gray-500 dark:text-gray-300">{{ slice $commit.This 0 8 }}</a>
··· 35 {{ end }} 36 37 <span class="px-1 select-none before:content-['\00B7']"></span> 38 + {{ template "repo/fragments/time" $commit.Committer.When }} 39 <span class="px-1 select-none before:content-['\00B7']"></span> 40 41 <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
··· 122 l.Error("failed to get email to did map", "err", err) 123 } 124 125 - vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, commitsTrunc) 126 if err != nil { 127 l.Error("failed to GetVerifiedObjectCommits", "err", err) 128 }
··· 122 l.Error("failed to get email to did map", "err", err) 123 } 124 125 + vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, commitsTrunc) 126 if err != nil { 127 l.Error("failed to GetVerifiedObjectCommits", "err", err) 128 }
+2 -2
appview/repo/log.go
··· 116 l.Error("failed to fetch email to did mapping", "err", err) 117 } 118 119 - vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, xrpcResp.Commits) 120 if err != nil { 121 l.Error("failed to GetVerifiedObjectCommits", "err", err) 122 } ··· 192 l.Error("failed to get email to did mapping", "err", err) 193 } 194 195 - vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, []types.NiceDiff{*result.Diff}) 196 if err != nil { 197 l.Error("failed to GetVerifiedCommits", "err", err) 198 }
··· 116 l.Error("failed to fetch email to did mapping", "err", err) 117 } 118 119 + vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, xrpcResp.Commits) 120 if err != nil { 121 l.Error("failed to GetVerifiedObjectCommits", "err", err) 122 } ··· 192 l.Error("failed to get email to did mapping", "err", err) 193 } 194 195 + vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, []types.Commit{result.Diff.Commit}) 196 if err != nil { 197 l.Error("failed to GetVerifiedCommits", "err", err) 198 }
+1 -3
appview/repo/repo_util.go
··· 8 "tangled.org/core/appview/db" 9 "tangled.org/core/appview/models" 10 "tangled.org/core/types" 11 - 12 - "github.com/go-git/go-git/v5/plumbing/object" 13 ) 14 15 func sortFiles(files []types.NiceTree) { ··· 42 }) 43 } 44 45 - func uniqueEmails(commits []*object.Commit) []string { 46 emails := make(map[string]struct{}) 47 for _, commit := range commits { 48 if commit.Author.Email != "" {
··· 8 "tangled.org/core/appview/db" 9 "tangled.org/core/appview/models" 10 "tangled.org/core/types" 11 ) 12 13 func sortFiles(files []types.NiceTree) { ··· 40 }) 41 } 42 43 + func uniqueEmails(commits []types.Commit) []string { 44 emails := make(map[string]struct{}) 45 for _, commit := range commits { 46 if commit.Author.Email != "" {
+1 -34
crypto/verify.go
··· 5 "crypto/sha256" 6 "encoding/base64" 7 "fmt" 8 - "strings" 9 10 "github.com/hiddeco/sshsig" 11 "golang.org/x/crypto/ssh" 12 - "tangled.org/core/types" 13 ) 14 15 func VerifySignature(pubKey, signature, payload []byte) (error, bool) { ··· 28 // multiple algorithms but sha-512 is most secure, and git's ssh signing defaults 29 // to sha-512 for all key types anyway. 30 err = sshsig.Verify(buf, sig, pub, sshsig.HashSHA512, "git") 31 - return err, err == nil 32 - } 33 34 - // VerifyCommitSignature reconstructs the payload used to sign a commit. This is 35 - // essentially the git cat-file output but without the gpgsig header. 36 - // 37 - // Caveats: signature verification will fail on commits with more than one parent, 38 - // i.e. merge commits, because types.NiceDiff doesn't carry more than one Parent field 39 - // and we are unable to reconstruct the payload correctly. 40 - // 41 - // Ideally this should directly operate on an *object.Commit. 42 - func VerifyCommitSignature(pubKey string, commit types.NiceDiff) (error, bool) { 43 - signature := commit.Commit.PGPSignature 44 - 45 - author := bytes.NewBuffer([]byte{}) 46 - committer := bytes.NewBuffer([]byte{}) 47 - commit.Commit.Author.Encode(author) 48 - commit.Commit.Committer.Encode(committer) 49 - 50 - payload := strings.Builder{} 51 - 52 - fmt.Fprintf(&payload, "tree %s\n", commit.Commit.Tree) 53 - if commit.Commit.Parent != "" { 54 - fmt.Fprintf(&payload, "parent %s\n", commit.Commit.Parent) 55 - } 56 - fmt.Fprintf(&payload, "author %s\n", author.String()) 57 - fmt.Fprintf(&payload, "committer %s\n", committer.String()) 58 - if commit.Commit.ChangedId != "" { 59 - fmt.Fprintf(&payload, "change-id %s\n", commit.Commit.ChangedId) 60 - } 61 - fmt.Fprintf(&payload, "\n%s", commit.Commit.Message) 62 - 63 - return VerifySignature([]byte(pubKey), []byte(signature), []byte(payload.String())) 64 } 65 66 // SSHFingerprint computes the fingerprint of the supplied ssh pubkey.
··· 5 "crypto/sha256" 6 "encoding/base64" 7 "fmt" 8 9 "github.com/hiddeco/sshsig" 10 "golang.org/x/crypto/ssh" 11 ) 12 13 func VerifySignature(pubKey, signature, payload []byte) (error, bool) { ··· 26 // multiple algorithms but sha-512 is most secure, and git's ssh signing defaults 27 // to sha-512 for all key types anyway. 28 err = sshsig.Verify(buf, sig, pub, sshsig.HashSHA512, "git") 29 30 + return err, err == nil 31 } 32 33 // SSHFingerprint computes the fingerprint of the supplied ssh pubkey.
+1 -17
knotserver/git/diff.go
··· 77 nd.Diff = append(nd.Diff, ndiff) 78 } 79 80 - nd.Stat.FilesChanged = len(diffs) 81 - nd.Commit.This = c.Hash.String() 82 - nd.Commit.PGPSignature = c.PGPSignature 83 - nd.Commit.Committer = c.Committer 84 - nd.Commit.Tree = c.TreeHash.String() 85 - 86 - if parent.Hash.IsZero() { 87 - nd.Commit.Parent = "" 88 - } else { 89 - nd.Commit.Parent = parent.Hash.String() 90 - } 91 - nd.Commit.Author = c.Author 92 - nd.Commit.Message = c.Message 93 - 94 - if v, ok := c.ExtraHeaders["change-id"]; ok { 95 - nd.Commit.ChangedId = string(v) 96 - } 97 98 return &nd, nil 99 }
··· 77 nd.Diff = append(nd.Diff, ndiff) 78 } 79 80 + nd.Commit.FromGoGitCommit(c) 81 82 return &nd, nil 83 }
+6 -1
knotserver/xrpc/repo_log.go
··· 62 return 63 } 64 65 // Create response using existing types.RepoLogResponse 66 response := types.RepoLogResponse{ 67 - Commits: commits, 68 Ref: ref, 69 Page: (offset / limit) + 1, 70 PerPage: limit,
··· 62 return 63 } 64 65 + tcommits := make([]types.Commit, len(commits)) 66 + for i, c := range commits { 67 + tcommits[i].FromGoGitCommit(c) 68 + } 69 + 70 // Create response using existing types.RepoLogResponse 71 response := types.RepoLogResponse{ 72 + Commits: tcommits, 73 Ref: ref, 74 Page: (offset / limit) + 1, 75 PerPage: limit,
-1
patchutil/patchutil.go
··· 296 } 297 298 nd := types.NiceDiff{} 299 - nd.Commit.Parent = targetBranch 300 301 for _, d := range diffs { 302 ndiff := types.Diff{}
··· 296 } 297 298 nd := types.NiceDiff{} 299 300 for _, d := range diffs { 301 ndiff := types.Diff{}
+2 -12
types/diff.go
··· 2 3 import ( 4 "github.com/bluekeyes/go-gitdiff/gitdiff" 5 - "github.com/go-git/go-git/v5/plumbing/object" 6 ) 7 8 type DiffOpts struct { ··· 43 44 // A nicer git diff representation. 45 type NiceDiff struct { 46 - Commit struct { 47 - Message string `json:"message"` 48 - Author object.Signature `json:"author"` 49 - This string `json:"this"` 50 - Parent string `json:"parent"` 51 - PGPSignature string `json:"pgp_signature"` 52 - Committer object.Signature `json:"committer"` 53 - Tree string `json:"tree"` 54 - ChangedId string `json:"change_id"` 55 - } `json:"commit"` 56 - Stat struct { 57 FilesChanged int `json:"files_changed"` 58 Insertions int `json:"insertions"` 59 Deletions int `json:"deletions"`
··· 2 3 import ( 4 "github.com/bluekeyes/go-gitdiff/gitdiff" 5 ) 6 7 type DiffOpts struct { ··· 42 43 // A nicer git diff representation. 44 type NiceDiff struct { 45 + Commit Commit `json:"commit"` 46 + Stat struct { 47 FilesChanged int `json:"files_changed"` 48 Insertions int `json:"insertions"` 49 Deletions int `json:"deletions"`
+17 -17
types/repo.go
··· 8 ) 9 10 type RepoIndexResponse struct { 11 - IsEmpty bool `json:"is_empty"` 12 - Ref string `json:"ref,omitempty"` 13 - Readme string `json:"readme,omitempty"` 14 - ReadmeFileName string `json:"readme_file_name,omitempty"` 15 - Commits []*object.Commit `json:"commits,omitempty"` 16 - Description string `json:"description,omitempty"` 17 - Files []NiceTree `json:"files,omitempty"` 18 - Branches []Branch `json:"branches,omitempty"` 19 - Tags []*TagReference `json:"tags,omitempty"` 20 - TotalCommits int `json:"total_commits,omitempty"` 21 } 22 23 type RepoLogResponse struct { 24 - Commits []*object.Commit `json:"commits,omitempty"` 25 - Ref string `json:"ref,omitempty"` 26 - Description string `json:"description,omitempty"` 27 - Log bool `json:"log,omitempty"` 28 - Total int `json:"total,omitempty"` 29 - Page int `json:"page,omitempty"` 30 - PerPage int `json:"per_page,omitempty"` 31 } 32 33 type RepoCommitResponse struct {
··· 8 ) 9 10 type RepoIndexResponse struct { 11 + IsEmpty bool `json:"is_empty"` 12 + Ref string `json:"ref,omitempty"` 13 + Readme string `json:"readme,omitempty"` 14 + ReadmeFileName string `json:"readme_file_name,omitempty"` 15 + Commits []Commit `json:"commits,omitempty"` 16 + Description string `json:"description,omitempty"` 17 + Files []NiceTree `json:"files,omitempty"` 18 + Branches []Branch `json:"branches,omitempty"` 19 + Tags []*TagReference `json:"tags,omitempty"` 20 + TotalCommits int `json:"total_commits,omitempty"` 21 } 22 23 type RepoLogResponse struct { 24 + Commits []Commit `json:"commits,omitempty"` 25 + Ref string `json:"ref,omitempty"` 26 + Description string `json:"description,omitempty"` 27 + Log bool `json:"log,omitempty"` 28 + Total int `json:"total,omitempty"` 29 + Page int `json:"page,omitempty"` 30 + PerPage int `json:"per_page,omitempty"` 31 } 32 33 type RepoCommitResponse struct {