Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
6.9 KiB
247 lines
6.9 KiB
// Copyright 2019 The Gitea Authors. All rights reserved. |
|
// Use of this source code is governed by a MIT-style |
|
// license that can be found in the LICENSE file. |
|
|
|
package models |
|
|
|
import ( |
|
"strings" |
|
|
|
"code.gitea.io/gitea/modules/git" |
|
"code.gitea.io/gitea/modules/log" |
|
"code.gitea.io/gitea/modules/process" |
|
"code.gitea.io/gitea/modules/setting" |
|
) |
|
|
|
type signingMode string |
|
|
|
const ( |
|
never signingMode = "never" |
|
always signingMode = "always" |
|
pubkey signingMode = "pubkey" |
|
twofa signingMode = "twofa" |
|
parentSigned signingMode = "parentsigned" |
|
baseSigned signingMode = "basesigned" |
|
headSigned signingMode = "headsigned" |
|
commitsSigned signingMode = "commitssigned" |
|
approved signingMode = "approved" |
|
noKey signingMode = "nokey" |
|
) |
|
|
|
func signingModeFromStrings(modeStrings []string) []signingMode { |
|
returnable := make([]signingMode, 0, len(modeStrings)) |
|
for _, mode := range modeStrings { |
|
signMode := signingMode(strings.ToLower(strings.TrimSpace(mode))) |
|
switch signMode { |
|
case never: |
|
return []signingMode{never} |
|
case always: |
|
return []signingMode{always} |
|
case pubkey: |
|
fallthrough |
|
case twofa: |
|
fallthrough |
|
case parentSigned: |
|
fallthrough |
|
case baseSigned: |
|
fallthrough |
|
case headSigned: |
|
fallthrough |
|
case approved: |
|
fallthrough |
|
case commitsSigned: |
|
returnable = append(returnable, signMode) |
|
} |
|
} |
|
if len(returnable) == 0 { |
|
return []signingMode{never} |
|
} |
|
return returnable |
|
} |
|
|
|
// SigningKey returns the KeyID and git Signature for the repo |
|
func SigningKey(repoPath string) (string, *git.Signature) { |
|
if setting.Repository.Signing.SigningKey == "none" { |
|
return "", nil |
|
} |
|
|
|
if setting.Repository.Signing.SigningKey == "default" || setting.Repository.Signing.SigningKey == "" { |
|
// Can ignore the error here as it means that commit.gpgsign is not set |
|
value, _ := git.NewCommand("config", "--get", "commit.gpgsign").RunInDir(repoPath) |
|
sign, valid := git.ParseBool(strings.TrimSpace(value)) |
|
if !sign || !valid { |
|
return "", nil |
|
} |
|
|
|
signingKey, _ := git.NewCommand("config", "--get", "user.signingkey").RunInDir(repoPath) |
|
signingName, _ := git.NewCommand("config", "--get", "user.name").RunInDir(repoPath) |
|
signingEmail, _ := git.NewCommand("config", "--get", "user.email").RunInDir(repoPath) |
|
return strings.TrimSpace(signingKey), &git.Signature{ |
|
Name: strings.TrimSpace(signingName), |
|
Email: strings.TrimSpace(signingEmail), |
|
} |
|
} |
|
|
|
return setting.Repository.Signing.SigningKey, &git.Signature{ |
|
Name: setting.Repository.Signing.SigningName, |
|
Email: setting.Repository.Signing.SigningEmail, |
|
} |
|
} |
|
|
|
// PublicSigningKey gets the public signing key within a provided repository directory |
|
func PublicSigningKey(repoPath string) (string, error) { |
|
signingKey, _ := SigningKey(repoPath) |
|
if signingKey == "" { |
|
return "", nil |
|
} |
|
|
|
content, stderr, err := process.GetManager().ExecDir(-1, repoPath, |
|
"gpg --export -a", "gpg", "--export", "-a", signingKey) |
|
if err != nil { |
|
log.Error("Unable to get default signing key in %s: %s, %s, %v", repoPath, signingKey, stderr, err) |
|
return "", err |
|
} |
|
return content, nil |
|
} |
|
|
|
// SignInitialCommit determines if we should sign the initial commit to this repository |
|
func SignInitialCommit(repoPath string, u *User) (bool, string, *git.Signature, error) { |
|
rules := signingModeFromStrings(setting.Repository.Signing.InitialCommit) |
|
signingKey, sig := SigningKey(repoPath) |
|
if signingKey == "" { |
|
return false, "", nil, &ErrWontSign{noKey} |
|
} |
|
|
|
Loop: |
|
for _, rule := range rules { |
|
switch rule { |
|
case never: |
|
return false, "", nil, &ErrWontSign{never} |
|
case always: |
|
break Loop |
|
case pubkey: |
|
keys, err := ListGPGKeys(u.ID, ListOptions{}) |
|
if err != nil { |
|
return false, "", nil, err |
|
} |
|
if len(keys) == 0 { |
|
return false, "", nil, &ErrWontSign{pubkey} |
|
} |
|
case twofa: |
|
twofaModel, err := GetTwoFactorByUID(u.ID) |
|
if err != nil && !IsErrTwoFactorNotEnrolled(err) { |
|
return false, "", nil, err |
|
} |
|
if twofaModel == nil { |
|
return false, "", nil, &ErrWontSign{twofa} |
|
} |
|
} |
|
} |
|
return true, signingKey, sig, nil |
|
} |
|
|
|
// SignWikiCommit determines if we should sign the commits to this repository wiki |
|
func (repo *Repository) SignWikiCommit(u *User) (bool, string, *git.Signature, error) { |
|
rules := signingModeFromStrings(setting.Repository.Signing.Wiki) |
|
signingKey, sig := SigningKey(repo.WikiPath()) |
|
if signingKey == "" { |
|
return false, "", nil, &ErrWontSign{noKey} |
|
} |
|
|
|
Loop: |
|
for _, rule := range rules { |
|
switch rule { |
|
case never: |
|
return false, "", nil, &ErrWontSign{never} |
|
case always: |
|
break Loop |
|
case pubkey: |
|
keys, err := ListGPGKeys(u.ID, ListOptions{}) |
|
if err != nil { |
|
return false, "", nil, err |
|
} |
|
if len(keys) == 0 { |
|
return false, "", nil, &ErrWontSign{pubkey} |
|
} |
|
case twofa: |
|
twofaModel, err := GetTwoFactorByUID(u.ID) |
|
if err != nil && !IsErrTwoFactorNotEnrolled(err) { |
|
return false, "", nil, err |
|
} |
|
if twofaModel == nil { |
|
return false, "", nil, &ErrWontSign{twofa} |
|
} |
|
case parentSigned: |
|
gitRepo, err := git.OpenRepository(repo.WikiPath()) |
|
if err != nil { |
|
return false, "", nil, err |
|
} |
|
defer gitRepo.Close() |
|
commit, err := gitRepo.GetCommit("HEAD") |
|
if err != nil { |
|
return false, "", nil, err |
|
} |
|
if commit.Signature == nil { |
|
return false, "", nil, &ErrWontSign{parentSigned} |
|
} |
|
verification := ParseCommitWithSignature(commit) |
|
if !verification.Verified { |
|
return false, "", nil, &ErrWontSign{parentSigned} |
|
} |
|
} |
|
} |
|
return true, signingKey, sig, nil |
|
} |
|
|
|
// SignCRUDAction determines if we should sign a CRUD commit to this repository |
|
func (repo *Repository) SignCRUDAction(u *User, tmpBasePath, parentCommit string) (bool, string, *git.Signature, error) { |
|
rules := signingModeFromStrings(setting.Repository.Signing.CRUDActions) |
|
signingKey, sig := SigningKey(repo.RepoPath()) |
|
if signingKey == "" { |
|
return false, "", nil, &ErrWontSign{noKey} |
|
} |
|
|
|
Loop: |
|
for _, rule := range rules { |
|
switch rule { |
|
case never: |
|
return false, "", nil, &ErrWontSign{never} |
|
case always: |
|
break Loop |
|
case pubkey: |
|
keys, err := ListGPGKeys(u.ID, ListOptions{}) |
|
if err != nil { |
|
return false, "", nil, err |
|
} |
|
if len(keys) == 0 { |
|
return false, "", nil, &ErrWontSign{pubkey} |
|
} |
|
case twofa: |
|
twofaModel, err := GetTwoFactorByUID(u.ID) |
|
if err != nil && !IsErrTwoFactorNotEnrolled(err) { |
|
return false, "", nil, err |
|
} |
|
if twofaModel == nil { |
|
return false, "", nil, &ErrWontSign{twofa} |
|
} |
|
case parentSigned: |
|
gitRepo, err := git.OpenRepository(tmpBasePath) |
|
if err != nil { |
|
return false, "", nil, err |
|
} |
|
defer gitRepo.Close() |
|
commit, err := gitRepo.GetCommit(parentCommit) |
|
if err != nil { |
|
return false, "", nil, err |
|
} |
|
if commit.Signature == nil { |
|
return false, "", nil, &ErrWontSign{parentSigned} |
|
} |
|
verification := ParseCommitWithSignature(commit) |
|
if !verification.Verified { |
|
return false, "", nil, &ErrWontSign{parentSigned} |
|
} |
|
} |
|
} |
|
return true, signingKey, sig, nil |
|
}
|
|
|