forked from NYANDEV/forgejo
b1e8858de9
- When someone really wants to avoid sharing their email, they could
configure git to use the noreply email for git commits. However if they
also wanted to use SSH signing, it would not show up as verified as the
noreply email address was technically not an activated email address for
the user.
- Add unit tests for the `ParseCommitWithSSHSignature` function.
- Resolves https://codeberg.org/Codeberg/Community/issues/946
(cherry picked from commit 1685de7eba
)
83 lines
2.4 KiB
Go
83 lines
2.4 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package asymkey
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/git"
|
|
"code.gitea.io/gitea/modules/log"
|
|
|
|
"github.com/42wim/sshsig"
|
|
)
|
|
|
|
// ParseCommitWithSSHSignature check if signature is good against keystore.
|
|
func ParseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committer *user_model.User) *CommitVerification {
|
|
// Now try to associate the signature with the committer, if present
|
|
if committer.ID != 0 {
|
|
keys, err := ListPublicKeys(ctx, committer.ID, db.ListOptions{})
|
|
if err != nil { // Skipping failed to get ssh keys of user
|
|
log.Error("ListPublicKeys: %v", err)
|
|
return &CommitVerification{
|
|
CommittingUser: committer,
|
|
Verified: false,
|
|
Reason: "gpg.error.failed_retrieval_gpg_keys",
|
|
}
|
|
}
|
|
|
|
committerEmailAddresses, err := user_model.GetEmailAddresses(ctx, committer.ID)
|
|
if err != nil {
|
|
log.Error("GetEmailAddresses: %v", err)
|
|
}
|
|
|
|
// Add the noreply email address as verified address.
|
|
committerEmailAddresses = append(committerEmailAddresses, &user_model.EmailAddress{
|
|
IsActivated: true,
|
|
Email: committer.GetPlaceholderEmail(),
|
|
})
|
|
|
|
activated := false
|
|
for _, e := range committerEmailAddresses {
|
|
if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
|
|
activated = true
|
|
break
|
|
}
|
|
}
|
|
|
|
for _, k := range keys {
|
|
if k.Verified && activated {
|
|
commitVerification := verifySSHCommitVerification(c.Signature.Signature, c.Signature.Payload, k, committer, committer, c.Committer.Email)
|
|
if commitVerification != nil {
|
|
return commitVerification
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return &CommitVerification{
|
|
CommittingUser: committer,
|
|
Verified: false,
|
|
Reason: NoKeyFound,
|
|
}
|
|
}
|
|
|
|
func verifySSHCommitVerification(sig, payload string, k *PublicKey, committer, signer *user_model.User, email string) *CommitVerification {
|
|
if err := sshsig.Verify(bytes.NewBuffer([]byte(payload)), []byte(sig), []byte(k.Content), "git"); err != nil {
|
|
return nil
|
|
}
|
|
|
|
return &CommitVerification{ // Everything is ok
|
|
CommittingUser: committer,
|
|
Verified: true,
|
|
Reason: fmt.Sprintf("%s / %s", signer.Name, k.Fingerprint),
|
|
SigningUser: signer,
|
|
SigningSSHKey: k,
|
|
SigningEmail: email,
|
|
}
|
|
}
|