From dbff91b518a3fd6d23a791f98cfed89dd85e577c Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sun, 31 May 2020 16:31:22 +0100 Subject: [PATCH 1/5] Add configurable Trust Models Gitea's default signature verification model differs from GitHub. GitHub uses signatures to verify that the committer is who they say they are - meaning that when GitHub makes a signed commit it must be the committer. The GitHub model prevents re-publishing of commits after revocation of a key and prevents re-signing of other people's commits to create a completely trusted repository signed by one key or a set of trusted keys. The default behaviour of Gitea in contrast is to always display the avatar and information related to a signature. This allows signatures to be decoupled from the committer. That being said, allowing arbitary users to present other peoples commits as theirs is not necessarily desired therefore we have a trust model whereby signatures from collaborators are marked trusted, signatures matching the commit line are marked untrusted and signatures that match a user in the db but not the committer line are marked unmatched. The problem with this model is that this conflicts with Github therefore we need to provide an option to allow users to choose the Github model should they wish to. Signed-off-by: Andrew Thornton --- custom/conf/app.ini.sample | 2 + .../doc/advanced/config-cheat-sheet.en-us.md | 4 + models/gpg_key.go | 89 +++++++++++++----- models/pull_sign.go | 50 +++++----- models/repo.go | 56 +++++++++++ models/repo_sign.go | 93 ++++++++++--------- modules/auth/repo_form.go | 4 + modules/context/repo.go | 2 +- modules/git/repo_tree.go | 10 +- modules/repofiles/delete.go | 2 +- modules/repofiles/temp_repo.go | 22 ++++- modules/repofiles/update.go | 2 +- modules/repository/create.go | 1 + modules/repository/generate.go | 1 + modules/repository/init.go | 17 +++- modules/setting/repository.go | 50 +++++----- modules/structs/repo.go | 3 + options/locale/locale_en-US.ini | 13 +++ routers/api/v1/repo/repo.go | 1 + routers/repo/issue.go | 2 +- routers/repo/repo.go | 1 + routers/repo/setting.go | 25 +++++ services/pull/merge.go | 19 +++- services/wiki/wiki.go | 19 +++- templates/repo/create.tmpl | 13 +++ templates/repo/settings/options.tmpl | 46 +++++++++ templates/swagger/v1_json.tmpl | 11 +++ 27 files changed, 422 insertions(+), 136 deletions(-) diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index 5e150172d5bf..f57480c51002 100644 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -110,6 +110,8 @@ SIGNING_KEY = default ; by setting the SIGNING_KEY ID to the correct ID.) SIGNING_NAME = SIGNING_EMAIL = +; Sets the default trust model for repositories. Options are: collaborator, committer, collaboratorcommitter +DEFAULT_TRUST_MODEL=collaborator ; Determines when gitea should sign the initial commit when creating a repository ; Either: ; - never diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index f0908c22a34f..29006951b1bc 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -98,6 +98,10 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. - `twofa`: Only sign if the user is logged in with twofa - `always`: Always sign - Options other than `never` and `always` can be combined as a comma separated list. +- `DEFAULT_TRUST_MODEL`: **collaborator**: \[collaborator, committer, collaboratorcommitter\]: The default trust model used for verifying commits. + - `collaborator`: Trust signatures signed by keys of collaborators. + - `committer`: Trust signatures that match committers (This matches GitHub and will force Gitea signed commits to have Gitea as the commmitter). + - `collaboratorcommitter`: Trust signatures signed by keys of collaborators which match the commiter. - `WIKI`: **never**: \[never, pubkey, twofa, always, parentsigned\]: Sign commits to wiki. - `CRUD_ACTIONS`: **pubkey, twofa, parentsigned**: \[never, pubkey, twofa, parentsigned, always\]: Sign CRUD actions. - Options as above, with the addition of: diff --git a/models/gpg_key.go b/models/gpg_key.go index bebd33191aa1..9a82ae7033b1 100644 --- a/models/gpg_key.go +++ b/models/gpg_key.go @@ -765,7 +765,7 @@ func ParseCommitsWithSignature(oldCommits *list.List, repository *Repository) *l newCommits = list.New() e = oldCommits.Front() ) - memberMap := map[int64]bool{} + keyMap := map[string]bool{} for e != nil { c := e.Value.(UserCommit) @@ -774,7 +774,7 @@ func ParseCommitsWithSignature(oldCommits *list.List, repository *Repository) *l Verification: ParseCommitWithSignature(c.Commit), } - _ = CalculateTrustStatus(signCommit.Verification, repository, &memberMap) + _ = CalculateTrustStatus(signCommit.Verification, repository, &keyMap) newCommits.PushBack(signCommit) e = e.Next() @@ -783,31 +783,72 @@ func ParseCommitsWithSignature(oldCommits *list.List, repository *Repository) *l } // CalculateTrustStatus will calculate the TrustStatus for a commit verification within a repository -func CalculateTrustStatus(verification *CommitVerification, repository *Repository, memberMap *map[int64]bool) (err error) { - if verification.Verified { +func CalculateTrustStatus(verification *CommitVerification, repository *Repository, keyMap *map[string]bool) (err error) { + if !verification.Verified { + return + } + + // There are several trust models in Gitea + trustModel := repository.GetTrustModel() + + // In the Committer trust model a signature is trusted if it matches the committer + // - it doesn't matter if they're a collaborator, the owner, Gitea or Github + // NB: This model is commit verification only + if trustModel == CommitterTrustModel { + // default to "unmatched" + verification.TrustStatus = "unmatched" + + // We can only verify against users in our database but the default key will match + // against by email if it is not in the db. + if (verification.SigningUser.ID != 0 && + verification.CommittingUser.ID == verification.SigningUser.ID) || + (verification.SigningUser.ID == 0 && verification.CommittingUser.ID == 0 && + verification.SigningUser.Email == verification.CommittingUser.Email) { + verification.TrustStatus = "trusted" + } + return + } + + // Now we drop to the more nuanced trust models... + + if verification.SigningUser.ID == 0 { + // This commit is signed by the default key - but this key is not assigned to a user in the DB. verification.TrustStatus = "trusted" - if verification.SigningUser.ID != 0 { - var isMember bool - if memberMap != nil { - var has bool - isMember, has = (*memberMap)[verification.SigningUser.ID] - if !has { - isMember, err = repository.IsOwnerMemberCollaborator(verification.SigningUser.ID) - (*memberMap)[verification.SigningUser.ID] = isMember - } - } else { - isMember, err = repository.IsOwnerMemberCollaborator(verification.SigningUser.ID) - } - if !isMember { - verification.TrustStatus = "untrusted" - if verification.CommittingUser.ID != verification.SigningUser.ID { - // The committing user and the signing user are not the same and are not the default key - // This should be marked as questionable unless the signing user is a collaborator/team member etc. - verification.TrustStatus = "unmatched" - } - } + // However in the CollaboratorCommitterTrustModel we cannot mark this as trusted + // unless the default key matches the email of a non-user. + if trustModel == CollaboratorCommitterTrustModel && (verification.CommittingUser.ID != 0 || + verification.SigningUser.Email != verification.CommittingUser.Email) { + verification.TrustStatus = "untrusted" + } + return + } + + verification.TrustStatus = "trusted" + + var isMember bool + if keyMap != nil { + var has bool + isMember, has = (*keyMap)[verification.SigningKey.KeyID] + if !has { + isMember, err = repository.IsOwnerMemberCollaborator(verification.SigningUser.ID) + (*keyMap)[verification.SigningKey.KeyID] = isMember + } + } else { + isMember, err = repository.IsOwnerMemberCollaborator(verification.SigningUser.ID) + } + + if !isMember { + verification.TrustStatus = "untrusted" + if verification.CommittingUser.ID != verification.SigningUser.ID { + // The committing user and the signing user are not the same + // This should be marked as questionable unless the signing user is a collaborator/team member etc. + verification.TrustStatus = "unmatched" } + } else if trustModel == CollaboratorCommitterTrustModel && verification.CommittingUser.ID != verification.SigningUser.ID { + // The committing user and the signing user are not the same and our trustmodel states that they must match + verification.TrustStatus = "unmatched" } + return } diff --git a/models/pull_sign.go b/models/pull_sign.go index 91b8defa53ea..4818f36d4119 100644 --- a/models/pull_sign.go +++ b/models/pull_sign.go @@ -11,16 +11,16 @@ import ( ) // SignMerge determines if we should sign a PR merge commit to the base repository -func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit string) (bool, string, error) { +func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit string) (bool, string, *git.Signature, error) { if err := pr.LoadBaseRepo(); err != nil { log.Error("Unable to get Base Repo for pull request") - return false, "", err + return false, "", nil, err } repo := pr.BaseRepo - signingKey := signingKey(repo.RepoPath()) + signingKey, signer := SigningKey(repo.RepoPath()) if signingKey == "" { - return false, "", &ErrWontSign{noKey} + return false, "", nil, &ErrWontSign{noKey} } rules := signingModeFromStrings(setting.Repository.Signing.Merges) @@ -30,101 +30,101 @@ func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit st for _, rule := range rules { switch rule { case never: - return false, "", &ErrWontSign{never} + return false, "", nil, &ErrWontSign{never} case always: break case pubkey: keys, err := ListGPGKeys(u.ID, ListOptions{}) if err != nil { - return false, "", err + return false, "", nil, err } if len(keys) == 0 { - return false, "", &ErrWontSign{pubkey} + return false, "", nil, &ErrWontSign{pubkey} } case twofa: twofaModel, err := GetTwoFactorByUID(u.ID) if err != nil && !IsErrTwoFactorNotEnrolled(err) { - return false, "", err + return false, "", nil, err } if twofaModel == nil { - return false, "", &ErrWontSign{twofa} + return false, "", nil, &ErrWontSign{twofa} } case approved: protectedBranch, err := GetProtectedBranchBy(repo.ID, pr.BaseBranch) if err != nil { - return false, "", err + return false, "", nil, err } if protectedBranch == nil { - return false, "", &ErrWontSign{approved} + return false, "", nil, &ErrWontSign{approved} } if protectedBranch.GetGrantedApprovalsCount(pr) < 1 { - return false, "", &ErrWontSign{approved} + return false, "", nil, &ErrWontSign{approved} } case baseSigned: if gitRepo == nil { gitRepo, err = git.OpenRepository(tmpBasePath) if err != nil { - return false, "", err + return false, "", nil, err } defer gitRepo.Close() } commit, err := gitRepo.GetCommit(baseCommit) if err != nil { - return false, "", err + return false, "", nil, err } verification := ParseCommitWithSignature(commit) if !verification.Verified { - return false, "", &ErrWontSign{baseSigned} + return false, "", nil, &ErrWontSign{baseSigned} } case headSigned: if gitRepo == nil { gitRepo, err = git.OpenRepository(tmpBasePath) if err != nil { - return false, "", err + return false, "", nil, err } defer gitRepo.Close() } commit, err := gitRepo.GetCommit(headCommit) if err != nil { - return false, "", err + return false, "", nil, err } verification := ParseCommitWithSignature(commit) if !verification.Verified { - return false, "", &ErrWontSign{headSigned} + return false, "", nil, &ErrWontSign{headSigned} } case commitsSigned: if gitRepo == nil { gitRepo, err = git.OpenRepository(tmpBasePath) if err != nil { - return false, "", err + return false, "", nil, err } defer gitRepo.Close() } commit, err := gitRepo.GetCommit(headCommit) if err != nil { - return false, "", err + return false, "", nil, err } verification := ParseCommitWithSignature(commit) if !verification.Verified { - return false, "", &ErrWontSign{commitsSigned} + return false, "", nil, &ErrWontSign{commitsSigned} } // need to work out merge-base mergeBaseCommit, _, err := gitRepo.GetMergeBase("", baseCommit, headCommit) if err != nil { - return false, "", err + return false, "", nil, err } commitList, err := commit.CommitsBeforeUntil(mergeBaseCommit) if err != nil { - return false, "", err + return false, "", nil, err } for e := commitList.Front(); e != nil; e = e.Next() { commit = e.Value.(*git.Commit) verification := ParseCommitWithSignature(commit) if !verification.Verified { - return false, "", &ErrWontSign{commitsSigned} + return false, "", nil, &ErrWontSign{commitsSigned} } } } } - return true, signingKey, nil + return true, signingKey, signer, nil } diff --git a/models/repo.go b/models/repo.go index d31d262c8df3..82082b11252a 100644 --- a/models/repo.go +++ b/models/repo.go @@ -139,6 +139,47 @@ const ( RepositoryBeingMigrated // repository is migrating ) +// TrustModelType defines the types of trust model for this repository +type TrustModelType int + +// kinds of TrustModel +const ( + DefaultTrustModel TrustModelType = iota // default trust model + CommitterTrustModel + CollaboratorTrustModel + CollaboratorCommitterTrustModel +) + +// String converts a TrustModelType to a string +func (t TrustModelType) String() string { + switch t { + case DefaultTrustModel: + return "default" + case CommitterTrustModel: + return "committer" + case CollaboratorTrustModel: + return "collaborator" + case CollaboratorCommitterTrustModel: + return "collaboratorcommitter" + } + return "default" +} + +// ToTrustModel converts a string to a TrustModelType +func ToTrustModel(model string) TrustModelType { + switch strings.ToLower(strings.TrimSpace(model)) { + case "default": + return DefaultTrustModel + case "collaborator": + return CollaboratorTrustModel + case "committer": + return CommitterTrustModel + case "collaboratorcommitter": + return CollaboratorCommitterTrustModel + } + return DefaultTrustModel +} + // Repository represents a git repository. type Repository struct { ID int64 `xorm:"pk autoincr"` @@ -191,6 +232,8 @@ type Repository struct { CloseIssuesViaCommitInAnyBranch bool `xorm:"NOT NULL DEFAULT false"` Topics []string `xorm:"TEXT JSON"` + TrustModel TrustModelType + // Avatar: ID(10-20)-md5(32) - must fit into 64 symbols Avatar string `xorm:"VARCHAR(64)"` @@ -1018,6 +1061,7 @@ type CreateRepoOptions struct { IsMirror bool AutoInit bool Status RepositoryStatus + TrustModel TrustModelType } // GetRepoInitFile returns repository init files @@ -2335,3 +2379,15 @@ func updateRepositoryCols(e Engine, repo *Repository, cols ...string) error { func UpdateRepositoryCols(repo *Repository, cols ...string) error { return updateRepositoryCols(x, repo, cols...) } + +// GetTrustModel will get the TrustModel for the repo or the default trust model +func (repo *Repository) GetTrustModel() TrustModelType { + trustModel := repo.TrustModel + if trustModel == DefaultTrustModel { + trustModel = ToTrustModel(setting.Repository.Signing.DefaultTrustModel) + if trustModel == DefaultTrustModel { + return CollaboratorTrustModel + } + } + return trustModel +} diff --git a/models/repo_sign.go b/models/repo_sign.go index c728a2911a13..835df964ac7e 100644 --- a/models/repo_sign.go +++ b/models/repo_sign.go @@ -31,7 +31,7 @@ const ( func signingModeFromStrings(modeStrings []string) []signingMode { returnable := make([]signingMode, 0, len(modeStrings)) for _, mode := range modeStrings { - signMode := signingMode(strings.ToLower(mode)) + signMode := signingMode(strings.ToLower(strings.TrimSpace(mode))) switch signMode { case never: return []signingMode{never} @@ -59,9 +59,10 @@ func signingModeFromStrings(modeStrings []string) []signingMode { return returnable } -func signingKey(repoPath string) string { +// SigningKey returns the KeyID and git Signature for the repo +func SigningKey(repoPath string) (string, *git.Signature) { if setting.Repository.Signing.SigningKey == "none" { - return "" + return "", nil } if setting.Repository.Signing.SigningKey == "default" || setting.Repository.Signing.SigningKey == "" { @@ -69,19 +70,27 @@ func signingKey(repoPath string) string { value, _ := git.NewCommand("config", "--get", "commit.gpgsign").RunInDir(repoPath) sign, valid := git.ParseBool(strings.TrimSpace(value)) if !sign || !valid { - return "" + return "", nil } signingKey, _ := git.NewCommand("config", "--get", "user.signingkey").RunInDir(repoPath) - return strings.TrimSpace(signingKey) + 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 + 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) + signingKey, _ := SigningKey(repoPath) if signingKey == "" { return "", nil } @@ -96,140 +105,140 @@ func PublicSigningKey(repoPath string) (string, error) { } // SignInitialCommit determines if we should sign the initial commit to this repository -func SignInitialCommit(repoPath string, u *User) (bool, string, error) { +func SignInitialCommit(repoPath string, u *User) (bool, string, *git.Signature, error) { rules := signingModeFromStrings(setting.Repository.Signing.InitialCommit) - signingKey := signingKey(repoPath) + signingKey, sig := SigningKey(repoPath) if signingKey == "" { - return false, "", &ErrWontSign{noKey} + return false, "", nil, &ErrWontSign{noKey} } for _, rule := range rules { switch rule { case never: - return false, "", &ErrWontSign{never} + return false, "", nil, &ErrWontSign{never} case always: break case pubkey: keys, err := ListGPGKeys(u.ID, ListOptions{}) if err != nil { - return false, "", err + return false, "", nil, err } if len(keys) == 0 { - return false, "", &ErrWontSign{pubkey} + return false, "", nil, &ErrWontSign{pubkey} } case twofa: twofaModel, err := GetTwoFactorByUID(u.ID) if err != nil && !IsErrTwoFactorNotEnrolled(err) { - return false, "", err + return false, "", nil, err } if twofaModel == nil { - return false, "", &ErrWontSign{twofa} + return false, "", nil, &ErrWontSign{twofa} } } } - return true, signingKey, nil + 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, error) { +func (repo *Repository) SignWikiCommit(u *User) (bool, string, *git.Signature, error) { rules := signingModeFromStrings(setting.Repository.Signing.Wiki) - signingKey := signingKey(repo.WikiPath()) + signingKey, sig := SigningKey(repo.WikiPath()) if signingKey == "" { - return false, "", &ErrWontSign{noKey} + return false, "", nil, &ErrWontSign{noKey} } for _, rule := range rules { switch rule { case never: - return false, "", &ErrWontSign{never} + return false, "", nil, &ErrWontSign{never} case always: break case pubkey: keys, err := ListGPGKeys(u.ID, ListOptions{}) if err != nil { - return false, "", err + return false, "", nil, err } if len(keys) == 0 { - return false, "", &ErrWontSign{pubkey} + return false, "", nil, &ErrWontSign{pubkey} } case twofa: twofaModel, err := GetTwoFactorByUID(u.ID) if err != nil && !IsErrTwoFactorNotEnrolled(err) { - return false, "", err + return false, "", nil, err } if twofaModel == nil { - return false, "", &ErrWontSign{twofa} + return false, "", nil, &ErrWontSign{twofa} } case parentSigned: gitRepo, err := git.OpenRepository(repo.WikiPath()) if err != nil { - return false, "", err + return false, "", nil, err } defer gitRepo.Close() commit, err := gitRepo.GetCommit("HEAD") if err != nil { - return false, "", err + return false, "", nil, err } if commit.Signature == nil { - return false, "", &ErrWontSign{parentSigned} + return false, "", nil, &ErrWontSign{parentSigned} } verification := ParseCommitWithSignature(commit) if !verification.Verified { - return false, "", &ErrWontSign{parentSigned} + return false, "", nil, &ErrWontSign{parentSigned} } } } - return true, signingKey, nil + 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, error) { +func (repo *Repository) SignCRUDAction(u *User, tmpBasePath, parentCommit string) (bool, string, *git.Signature, error) { rules := signingModeFromStrings(setting.Repository.Signing.CRUDActions) - signingKey := signingKey(repo.RepoPath()) + signingKey, sig := SigningKey(repo.RepoPath()) if signingKey == "" { - return false, "", &ErrWontSign{noKey} + return false, "", nil, &ErrWontSign{noKey} } for _, rule := range rules { switch rule { case never: - return false, "", &ErrWontSign{never} + return false, "", nil, &ErrWontSign{never} case always: break case pubkey: keys, err := ListGPGKeys(u.ID, ListOptions{}) if err != nil { - return false, "", err + return false, "", nil, err } if len(keys) == 0 { - return false, "", &ErrWontSign{pubkey} + return false, "", nil, &ErrWontSign{pubkey} } case twofa: twofaModel, err := GetTwoFactorByUID(u.ID) if err != nil && !IsErrTwoFactorNotEnrolled(err) { - return false, "", err + return false, "", nil, err } if twofaModel == nil { - return false, "", &ErrWontSign{twofa} + return false, "", nil, &ErrWontSign{twofa} } case parentSigned: gitRepo, err := git.OpenRepository(tmpBasePath) if err != nil { - return false, "", err + return false, "", nil, err } defer gitRepo.Close() commit, err := gitRepo.GetCommit(parentCommit) if err != nil { - return false, "", err + return false, "", nil, err } if commit.Signature == nil { - return false, "", &ErrWontSign{parentSigned} + return false, "", nil, &ErrWontSign{parentSigned} } verification := ParseCommitWithSignature(commit) if !verification.Verified { - return false, "", &ErrWontSign{parentSigned} + return false, "", nil, &ErrWontSign{parentSigned} } } } - return true, signingKey, nil + return true, signingKey, sig, nil } diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 6c3421e4f7d8..f58792395194 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -45,6 +45,7 @@ type CreateRepoForm struct { Webhooks bool Avatar bool Labels bool + TrustModel string } // Validate validates the fields @@ -139,6 +140,9 @@ type RepoSettingForm struct { EnableIssueDependencies bool IsArchived bool + // Signing Settings + TrustModel string + // Admin settings EnableHealthCheck bool EnableCloseIssuesViaCommitInAnyBranch bool diff --git a/modules/context/repo.go b/modules/context/repo.go index 5ebed0eb7ea0..7e759dd16918 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -100,7 +100,7 @@ func (r *Repository) CanCommitToBranch(doer *models.User) (CanCommitToBranchResu requireSigned = protectedBranch.RequireSignedCommits } - sign, keyID, err := r.Repository.SignCRUDAction(doer, r.Repository.RepoPath(), git.BranchPrefix+r.BranchName) + sign, keyID, _, err := r.Repository.SignCRUDAction(doer, r.Repository.RepoPath(), git.BranchPrefix+r.BranchName) canCommit := r.CanEnableEditor() && userCanPush if requireSigned { diff --git a/modules/git/repo_tree.go b/modules/git/repo_tree.go index 8f91f4efaccb..307d0f7a9d4c 100644 --- a/modules/git/repo_tree.go +++ b/modules/git/repo_tree.go @@ -64,7 +64,7 @@ type CommitTreeOpts struct { } // CommitTree creates a commit from a given tree id for the user with provided message -func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOpts) (SHA1, error) { +func (repo *Repository) CommitTree(author *Signature, committer *Signature, tree *Tree, opts CommitTreeOpts) (SHA1, error) { binVersion, err := BinVersion() if err != nil { return SHA1{}, err @@ -74,11 +74,11 @@ func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOp // Because this may call hooks we should pass in the environment env := append(os.Environ(), - "GIT_AUTHOR_NAME="+sig.Name, - "GIT_AUTHOR_EMAIL="+sig.Email, + "GIT_AUTHOR_NAME="+author.Name, + "GIT_AUTHOR_EMAIL="+author.Email, "GIT_AUTHOR_DATE="+commitTimeStr, - "GIT_COMMITTER_NAME="+sig.Name, - "GIT_COMMITTER_EMAIL="+sig.Email, + "GIT_COMMITTER_NAME="+committer.Name, + "GIT_COMMITTER_EMAIL="+committer.Email, "GIT_COMMITTER_DATE="+commitTimeStr, ) cmd := NewCommand("commit-tree", tree.ID.String()) diff --git a/modules/repofiles/delete.go b/modules/repofiles/delete.go index 2ffc75e7c8e1..8343776c4716 100644 --- a/modules/repofiles/delete.go +++ b/modules/repofiles/delete.go @@ -67,7 +67,7 @@ func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepo } } if protectedBranch.RequireSignedCommits { - _, _, err := repo.SignCRUDAction(doer, repo.RepoPath(), opts.OldBranch) + _, _, _, err := repo.SignCRUDAction(doer, repo.RepoPath(), opts.OldBranch) if err != nil { if !models.IsErrWontSign(err) { return nil, err diff --git a/modules/repofiles/temp_repo.go b/modules/repofiles/temp_repo.go index 2b03db8b4a31..79739191bc0a 100644 --- a/modules/repofiles/temp_repo.go +++ b/modules/repofiles/temp_repo.go @@ -206,8 +206,6 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *models "GIT_AUTHOR_NAME="+authorSig.Name, "GIT_AUTHOR_EMAIL="+authorSig.Email, "GIT_AUTHOR_DATE="+authorDate.Format(time.RFC3339), - "GIT_COMMITTER_NAME="+committerSig.Name, - "GIT_COMMITTER_EMAIL="+committerSig.Email, "GIT_COMMITTER_DATE="+committerDate.Format(time.RFC3339), ) @@ -219,14 +217,32 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *models // Determine if we should sign if version.Compare(binVersion, "1.7.9", ">=") { - sign, keyID, _ := t.repo.SignCRUDAction(author, t.basePath, "HEAD") + sign, keyID, signer, _ := t.repo.SignCRUDAction(author, t.basePath, "HEAD") if sign { args = append(args, "-S"+keyID) + if t.repo.GetTrustModel() == models.CommitterTrustModel || t.repo.GetTrustModel() == models.CollaboratorCommitterTrustModel { + if committerSig.Name != authorSig.Name || committerSig.Email != authorSig.Email { + // Add trailers + _, _ = messageBytes.WriteString("\n") + _, _ = messageBytes.WriteString("Co-Authored-By: ") + _, _ = messageBytes.WriteString(committerSig.String()) + _, _ = messageBytes.WriteString("\n") + _, _ = messageBytes.WriteString("Co-Committed-By: ") + _, _ = messageBytes.WriteString(committerSig.String()) + _, _ = messageBytes.WriteString("\n") + } + committerSig = signer + } } else if version.Compare(binVersion, "2.0.0", ">=") { args = append(args, "--no-gpg-sign") } } + env = append(env, + "GIT_COMMITTER_NAME="+committerSig.Name, + "GIT_COMMITTER_EMAIL="+committerSig.Email, + ) + stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) if err := git.NewCommand(args...).RunInDirTimeoutEnvFullPipeline(env, -1, t.basePath, stdout, stderr, messageBytes); err != nil { diff --git a/modules/repofiles/update.go b/modules/repofiles/update.go index d65f61c8409e..823179e19e0a 100644 --- a/modules/repofiles/update.go +++ b/modules/repofiles/update.go @@ -163,7 +163,7 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up } } if protectedBranch.RequireSignedCommits { - _, _, err := repo.SignCRUDAction(doer, repo.RepoPath(), opts.OldBranch) + _, _, _, err := repo.SignCRUDAction(doer, repo.RepoPath(), opts.OldBranch) if err != nil { if !models.IsErrWontSign(err) { return nil, err diff --git a/modules/repository/create.go b/modules/repository/create.go index 2f7d10f0d111..12331900e5ca 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -37,6 +37,7 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (_ *m CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch, Status: opts.Status, IsEmpty: !opts.AutoInit, + TrustModel: opts.TrustModel, } err = models.WithTx(func(ctx models.DBContext) error { diff --git a/modules/repository/generate.go b/modules/repository/generate.go index 6d80488de786..cda315815156 100644 --- a/modules/repository/generate.go +++ b/modules/repository/generate.go @@ -242,6 +242,7 @@ func GenerateRepository(ctx models.DBContext, doer, owner *models.User, template IsEmpty: !opts.GitContent || templateRepo.IsEmpty, IsFsckEnabled: templateRepo.IsFsckEnabled, TemplateID: templateRepo.ID, + TrustModel: templateRepo.TrustModel, } if err = models.CreateRepository(ctx, doer, owner, generateRepo); err != nil { diff --git a/modules/repository/init.go b/modules/repository/init.go index f468ca0435fc..568d64527205 100644 --- a/modules/repository/init.go +++ b/modules/repository/init.go @@ -108,10 +108,10 @@ func initRepoCommit(tmpPath string, repo *models.Repository, u *models.User, def "GIT_AUTHOR_NAME="+sig.Name, "GIT_AUTHOR_EMAIL="+sig.Email, "GIT_AUTHOR_DATE="+commitTimeStr, - "GIT_COMMITTER_NAME="+sig.Name, - "GIT_COMMITTER_EMAIL="+sig.Email, "GIT_COMMITTER_DATE="+commitTimeStr, ) + committerName := sig.Name + committerEmail := sig.Email if stdout, err := git.NewCommand("add", "--all"). SetDescription(fmt.Sprintf("initRepoCommit (git add): %s", tmpPath)). @@ -131,14 +131,25 @@ func initRepoCommit(tmpPath string, repo *models.Repository, u *models.User, def } if version.Compare(binVersion, "1.7.9", ">=") { - sign, keyID, _ := models.SignInitialCommit(tmpPath, u) + sign, keyID, signer, _ := models.SignInitialCommit(tmpPath, u) if sign { args = append(args, "-S"+keyID) + + if repo.GetTrustModel() == models.CommitterTrustModel || repo.GetTrustModel() == models.CollaboratorCommitterTrustModel { + // need to set the committer to the KeyID owner + committerName = signer.Name + committerEmail = signer.Email + } } else if version.Compare(binVersion, "2.0.0", ">=") { args = append(args, "--no-gpg-sign") } } + env = append(env, + "GIT_COMMITTER_NAME="+committerName, + "GIT_COMMITTER_EMAIL="+committerEmail, + ) + if stdout, err := git.NewCommand(args...). SetDescription(fmt.Sprintf("initRepoCommit (git commit): %s", tmpPath)). RunInDirWithEnv(tmpPath, env); err != nil { diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 8af3eaaf4693..f99f5845b9f9 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -79,13 +79,14 @@ var ( } `ini:"repository.issue"` Signing struct { - SigningKey string - SigningName string - SigningEmail string - InitialCommit []string - CRUDActions []string `ini:"CRUD_ACTIONS"` - Merges []string - Wiki []string + SigningKey string + SigningName string + SigningEmail string + InitialCommit []string + CRUDActions []string `ini:"CRUD_ACTIONS"` + Merges []string + Wiki []string + DefaultTrustModel string } `ini:"repository.signing"` }{ AnsiCharset: "", @@ -168,21 +169,23 @@ var ( // Signing settings Signing: struct { - SigningKey string - SigningName string - SigningEmail string - InitialCommit []string - CRUDActions []string `ini:"CRUD_ACTIONS"` - Merges []string - Wiki []string + SigningKey string + SigningName string + SigningEmail string + InitialCommit []string + CRUDActions []string `ini:"CRUD_ACTIONS"` + Merges []string + Wiki []string + DefaultTrustModel string }{ - SigningKey: "default", - SigningName: "", - SigningEmail: "", - InitialCommit: []string{"always"}, - CRUDActions: []string{"pubkey", "twofa", "parentsigned"}, - Merges: []string{"pubkey", "twofa", "basesigned", "commitssigned"}, - Wiki: []string{"never"}, + SigningKey: "default", + SigningName: "", + SigningEmail: "", + InitialCommit: []string{"always"}, + CRUDActions: []string{"pubkey", "twofa", "parentsigned"}, + Merges: []string{"pubkey", "twofa", "basesigned", "commitssigned"}, + Wiki: []string{"never"}, + DefaultTrustModel: "collaborator", }, } RepoRootPath string @@ -222,6 +225,11 @@ func newRepository() { log.Fatal("Failed to map Repository.PullRequest settings: %v", err) } + Repository.Signing.DefaultTrustModel = strings.ToLower(strings.TrimSpace(Repository.Signing.DefaultTrustModel)) + if Repository.Signing.DefaultTrustModel == "default" { + Repository.Signing.DefaultTrustModel = "collaborator" + } + if !filepath.IsAbs(Repository.Upload.TempPath) { Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath) } diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 832d330e749a..ab0aaae4387a 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -114,6 +114,9 @@ type CreateRepoOption struct { Readme string `json:"readme"` // DefaultBranch of the repository (used when initializes and in template) DefaultBranch string `json:"default_branch" binding:"GitRefName;MaxSize(100)"` + // TrustModel of the repository + // enum: default,collaborator,committer,collaboratorcommitter + TrustModel string `json:"trust_model"` } // EditRepoOption options when editing a repository's properties diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 57ab4966a31e..15a6a3c3832d 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1372,6 +1372,19 @@ settings.transfer_desc = Transfer this repository to a user or to an organizatio settings.transfer_notices_1 = - You will lose access to the repository if you transfer it to an individual user. settings.transfer_notices_2 = - You will keep access to the repository if you transfer it to an organization that you (co-)own. settings.transfer_form_title = Enter the repository name as confirmation: +settings.signing_settings = Signing Verification Settings +settings.trust_model = Signature Trust Model +settings.trust_model.default = Default Trust Model +settings.trust_model.default.desc= Use the default repository trust model for this installation. +settings.trust_model.collaborator = Collaborator +settings.trust_model.collaborator.long = Collaborator: Trust signatures signed by keys of collaborators +settings.trust_model.collaborator.desc = Signatures from keys owned by collaborators of this repository will be trusted - (whether they match the Committer or not). Otherwise, commits will be marked untrusted if the signature matches the committer and unmatched if the signature otherwise verifies. +settings.trust_model.committer = Committer +settings.trust_model.committer.long = Committer: Trust signatures that match committers (This matches GitHub and will force Gitea signed commits to have Gitea as the commmitter) +settings.trust_model.committer.desc = Signatures will only be marked trusted if they match the committer. If they otherwise verify they will be marked unmatched. This will force Gitea to be marked as the committer on signed commits with the actual committer marked as Co-Authored-By: and Co-Committed-By: trailer in the commit. The default Gitea key must match a User in the database, +settings.trust_model.collaboratorcommitter = Collaborator+Committer +settings.trust_model.collaboratorcommitter.long = Collaborator+Committer: Trust signatures signed by keys of collaborators which match the commiter. +settings.trust_model.collaboratorcommitter.desc = Signatures of keys owned by collaborators of this repository will be marked trusted only if they match the committer. Otherwise, the repository public keyring. Otherwise, commits will be marked untrusted if the signature matches the committer and unmatched if the signature otherwise verifies. This will force Gitea to be marked as the committer on signed commits with the actual committer marked as Co-Authored-By: and Co-Committed-By: trailer in the commit. The default Gitea key must match a User in the database, settings.wiki_delete = Delete Wiki Data settings.wiki_delete_desc = Deleting repository wiki data is permanent and cannot be undone. settings.wiki_delete_notices_1 = - This will permanently delete and disable the repository wiki for %s. diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index e5055daa2b10..cc3325b61e2f 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -243,6 +243,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR IsPrivate: opt.Private, AutoInit: opt.AutoInit, DefaultBranch: opt.DefaultBranch, + TrustModel: models.ToTrustModel(opt.TrustModel), }) if err != nil { if models.IsErrRepoAlreadyExist(err) { diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 181ed59a8c1a..ff7ec14b008e 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -1091,7 +1091,7 @@ func ViewIssue(ctx *context.Context) { } ctx.Data["WillSign"] = false if ctx.User != nil { - sign, key, err := pull.SignMerge(ctx.User, pull.BaseRepo.RepoPath(), pull.BaseBranch, pull.GetGitRefName()) + sign, key, _, err := pull.SignMerge(ctx.User, pull.BaseRepo.RepoPath(), pull.BaseBranch, pull.GetGitRefName()) ctx.Data["WillSign"] = sign ctx.Data["SigningKey"] = key if err != nil { diff --git a/routers/repo/repo.go b/routers/repo/repo.go index b0bb608d09b1..49d989db5edf 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -243,6 +243,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { IsPrivate: form.Private || setting.Repository.ForcePrivate, DefaultBranch: form.DefaultBranch, AutoInit: form.AutoInit, + TrustModel: models.ToTrustModel(form.TrustModel), }) if err == nil { log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index dff13ff5b375..0fc6741c7f0e 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -51,6 +51,11 @@ func Settings(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsOptions"] = true ctx.Data["ForcePrivate"] = setting.Repository.ForcePrivate + + signing, _ := models.SigningKey(ctx.Repo.Repository.RepoPath()) + ctx.Data["SigningKeyAvailable"] = len(signing) > 0 + ctx.Data["SigningSettings"] = setting.Repository.Signing + ctx.HTML(200, tplSettingsOptions) } @@ -309,6 +314,26 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) ctx.Redirect(ctx.Repo.RepoLink + "/settings") + case "signing": + changed := false + + trustModel := models.ToTrustModel(form.TrustModel) + if trustModel != repo.TrustModel { + repo.TrustModel = trustModel + changed = true + } + + if changed { + if err := models.UpdateRepository(repo, false); err != nil { + ctx.ServerError("UpdateRepository", err) + return + } + } + log.Trace("Repository signing settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) + + ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings") + case "admin": if !ctx.User.IsAdmin { ctx.Error(403) diff --git a/services/pull/merge.go b/services/pull/merge.go index 47521ce14770..913bc568636c 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -211,18 +211,23 @@ func rawMerge(pr *models.PullRequest, doer *models.User, mergeStyle models.Merge outbuf.Reset() errbuf.Reset() + sig := doer.NewGitSig() + committer := sig + // Determine if we should sign signArg := "" if version.Compare(binVersion, "1.7.9", ">=") { - sign, keyID, _ := pr.SignMerge(doer, tmpBasePath, "HEAD", trackingBranch) + sign, keyID, signer, _ := pr.SignMerge(doer, tmpBasePath, "HEAD", trackingBranch) if sign { signArg = "-S" + keyID + if pr.BaseRepo.GetTrustModel() == models.CommitterTrustModel || pr.BaseRepo.GetTrustModel() == models.CollaboratorCommitterTrustModel { + committer = signer + } } else if version.Compare(binVersion, "2.0.0", ">=") { signArg = "--no-gpg-sign" } } - sig := doer.NewGitSig() commitTimeStr := time.Now().Format(time.RFC3339) // Because this may call hooks we should pass in the environment @@ -230,8 +235,8 @@ func rawMerge(pr *models.PullRequest, doer *models.User, mergeStyle models.Merge "GIT_AUTHOR_NAME="+sig.Name, "GIT_AUTHOR_EMAIL="+sig.Email, "GIT_AUTHOR_DATE="+commitTimeStr, - "GIT_COMMITTER_NAME="+sig.Name, - "GIT_COMMITTER_EMAIL="+sig.Email, + "GIT_COMMITTER_NAME="+committer.Name, + "GIT_COMMITTER_EMAIL="+committer.Email, "GIT_COMMITTER_DATE="+commitTimeStr, ) @@ -348,6 +353,10 @@ func rawMerge(pr *models.PullRequest, doer *models.User, mergeStyle models.Merge return "", fmt.Errorf("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) } } else { + if committer != sig { + // add trailer + message += fmt.Sprintf("\nCo-Authored-By: %s\nCo-Committed-By: %s\n", sig.String(), sig.String()) + } if err := git.NewCommand("commit", signArg, fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) return "", fmt.Errorf("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) @@ -528,7 +537,7 @@ func IsSignedIfRequired(pr *models.PullRequest, doer *models.User) (bool, error) return true, nil } - sign, _, err := pr.SignMerge(doer, pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName()) + sign, _, _, err := pr.SignMerge(doer, pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName()) return sign, err } diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index 3616823c5d6d..fab02bae0c4d 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -185,16 +185,22 @@ func updateWikiPage(doer *models.User, repo *models.Repository, oldWikiName, new Message: message, } - sign, signingKey, _ := repo.SignWikiCommit(doer) + committer := doer.NewGitSig() + + sign, signingKey, signer, _ := repo.SignWikiCommit(doer) if sign { commitTreeOpts.KeyID = signingKey + if repo.GetTrustModel() == models.CommitterTrustModel || repo.GetTrustModel() == models.CollaboratorCommitterTrustModel { + committer = signer + } } else { commitTreeOpts.NoGPGSign = true } if hasMasterBranch { commitTreeOpts.Parents = []string{"HEAD"} } - commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), tree, commitTreeOpts) + + commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), committer, tree, commitTreeOpts) if err != nil { log.Error("%v", err) return err @@ -302,14 +308,19 @@ func DeleteWikiPage(doer *models.User, repo *models.Repository, wikiName string) Parents: []string{"HEAD"}, } - sign, signingKey, _ := repo.SignWikiCommit(doer) + committer := doer.NewGitSig() + + sign, signingKey, signer, _ := repo.SignWikiCommit(doer) if sign { commitTreeOpts.KeyID = signingKey + if repo.GetTrustModel() == models.CommitterTrustModel || repo.GetTrustModel() == models.CollaboratorCommitterTrustModel { + committer = signer + } } else { commitTreeOpts.NoGPGSign = true } - commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), tree, commitTreeOpts) + commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), committer, tree, commitTreeOpts) if err != nil { return err } diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl index cad60126ef9c..2dbd20a8df87 100644 --- a/templates/repo/create.tmpl +++ b/templates/repo/create.tmpl @@ -167,6 +167,19 @@ +
+ +

diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index c674fcf7f962..23b82f68563e 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -325,6 +325,52 @@ +

+ {{.i18n.Tr "repo.settings.signing_settings"}} +

+
+
+ {{.CsrfTokenHtml}} + +
+ +
+
+ + +

{{.i18n.Tr "repo.settings.trust_model.default.desc"}}

+
+
+
+
+ + +

{{.i18n.Tr "repo.settings.trust_model.collaborator.desc"}}

+
+
+
+
+ + +

{{.i18n.Tr "repo.settings.trust_model.committer.desc"}}

+
+
+
+
+ + +

{{.i18n.Tr "repo.settings.trust_model.collaboratorcommitter.desc"}}

+
+
+
+ +
+
+ +
+
+
+ {{if .IsAdmin}}

{{.i18n.Tr "repo.settings.admin_settings"}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 70f12b083f5b..07bf39c69a7e 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -11482,6 +11482,17 @@ "description": "Readme of the repository to create", "type": "string", "x-go-name": "Readme" + }, + "trust_model": { + "description": "TrustModel of the repository", + "type": "string", + "enum": [ + "default", + "collaborator", + "committer", + "collaboratorcommitter" + ], + "x-go-name": "TrustModel" } }, "x-go-package": "code.gitea.io/gitea/modules/structs" From e4fe7093f3dce8064369b2bd83c4e87dc05685ac Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sat, 6 Jun 2020 15:12:29 +0100 Subject: [PATCH 2/5] Adjust locale strings Signed-off-by: Andrew Thornton --- options/locale/locale_en-US.ini | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index a995a5f131f0..2778459c5bbe 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1382,14 +1382,14 @@ settings.trust_model = Signature Trust Model settings.trust_model.default = Default Trust Model settings.trust_model.default.desc= Use the default repository trust model for this installation. settings.trust_model.collaborator = Collaborator -settings.trust_model.collaborator.long = Collaborator: Trust signatures signed by keys of collaborators -settings.trust_model.collaborator.desc = Signatures from keys owned by collaborators of this repository will be trusted - (whether they match the Committer or not). Otherwise, commits will be marked untrusted if the signature matches the committer and unmatched if the signature otherwise verifies. +settings.trust_model.collaborator.long = Collaborator: Trust signatures by collaborators +settings.trust_model.collaborator.desc = Valid signatures by collaborators of this repository will be marked "trusted" - (whether they match the committer or not). Otherwise, valid signatures will be marked "untrusted" if the signature matches the committer and "unmatched" if not. settings.trust_model.committer = Committer -settings.trust_model.committer.long = Committer: Trust signatures that match committers (This matches GitHub and will force Gitea signed commits to have Gitea as the commmitter) -settings.trust_model.committer.desc = Signatures will only be marked trusted if they match the committer. If they otherwise verify they will be marked unmatched. This will force Gitea to be marked as the committer on signed commits with the actual committer marked as Co-Authored-By: and Co-Committed-By: trailer in the commit. The default Gitea key must match a User in the database, +settings.trust_model.committer.long = Committer: Trust signatures that match committers (This matches GitHub and will force Gitea signed commits to have Gitea as the committer) +settings.trust_model.committer.desc = Valid signatures will only be marked "trusted" if they match the committer, otherwise they will be marked "unmatched". This will force Gitea to be the committer on signed commits with the actual committer marked as Co-Authored-By: and Co-Committed-By: trailer in the commit. The default Gitea key must match a User in the database. settings.trust_model.collaboratorcommitter = Collaborator+Committer -settings.trust_model.collaboratorcommitter.long = Collaborator+Committer: Trust signatures signed by keys of collaborators which match the commiter. -settings.trust_model.collaboratorcommitter.desc = Signatures of keys owned by collaborators of this repository will be marked trusted only if they match the committer. Otherwise, the repository public keyring. Otherwise, commits will be marked untrusted if the signature matches the committer and unmatched if the signature otherwise verifies. This will force Gitea to be marked as the committer on signed commits with the actual committer marked as Co-Authored-By: and Co-Committed-By: trailer in the commit. The default Gitea key must match a User in the database, +settings.trust_model.collaboratorcommitter.long = Collaborator+Committer: Trust signatures by collaborators which match the committer +settings.trust_model.collaboratorcommitter.desc = Valid signatures by collaborators of this repository will be marked "trusted" if they match the committer. Otherwise, valid signatures will be marked "untrusted" if the signature matches the committer and "unmatched" otherwise. This will force Gitea to be marked as the committer on signed commits with the actual committer marked as Co-Authored-By: and Co-Committed-By: trailer in the commit. The default Gitea key must match a User in the database, settings.wiki_delete = Delete Wiki Data settings.wiki_delete_desc = Deleting repository wiki data is permanent and cannot be undone. settings.wiki_delete_notices_1 = - This will permanently delete and disable the repository wiki for %s. From 6d65d0e75be6dbfcda23841c94f1906c9a2f472a Mon Sep 17 00:00:00 2001 From: zeripath Date: Wed, 26 Aug 2020 11:39:08 +0100 Subject: [PATCH 3/5] as per @6543 Co-authored-by: 6543 <6543@obermui.de> --- models/gpg_key.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/models/gpg_key.go b/models/gpg_key.go index cf0e1c3d8d47..c9e48f06e755 100644 --- a/models/gpg_key.go +++ b/models/gpg_key.go @@ -868,10 +868,10 @@ func CalculateTrustStatus(verification *CommitVerification, repository *Reposito } // Now we drop to the more nuanced trust models... + verification.TrustStatus = "trusted" if verification.SigningUser.ID == 0 { // This commit is signed by the default key - but this key is not assigned to a user in the DB. - verification.TrustStatus = "trusted" // However in the CollaboratorCommitterTrustModel we cannot mark this as trusted // unless the default key matches the email of a non-user. @@ -882,7 +882,6 @@ func CalculateTrustStatus(verification *CommitVerification, repository *Reposito return } - verification.TrustStatus = "trusted" var isMember bool if keyMap != nil { From 5b5f5a992e7ca16bb8f6ff462ef0b682f2664b65 Mon Sep 17 00:00:00 2001 From: zeripath Date: Wed, 26 Aug 2020 17:49:16 +0100 Subject: [PATCH 4/5] Update models/gpg_key.go --- models/gpg_key.go | 1 - 1 file changed, 1 deletion(-) diff --git a/models/gpg_key.go b/models/gpg_key.go index c9e48f06e755..048b07ef166d 100644 --- a/models/gpg_key.go +++ b/models/gpg_key.go @@ -882,7 +882,6 @@ func CalculateTrustStatus(verification *CommitVerification, repository *Reposito return } - var isMember bool if keyMap != nil { var has bool From fba6adcf711e1ac40b254ecdf269fbd2ccd2fc1a Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Fri, 18 Sep 2020 19:31:50 +0100 Subject: [PATCH 5/5] Add migration for repository Signed-off-by: Andrew Thornton --- models/migrations/migrations.go | 2 ++ models/migrations/v152.go | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 models/migrations/v152.go diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 5317cc5743d5..ea1bf596492f 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -237,6 +237,8 @@ var migrations = []Migration{ NewMigration("add primary key to repo_topic", addPrimaryKeyToRepoTopic), // v151 -> v152 NewMigration("set default password algorithm to Argon2", setDefaultPasswordToArgon2), + // v152 -> v153 + NewMigration("add TrustModel field to Repository", addTrustModelToRepository), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v152.go b/models/migrations/v152.go new file mode 100644 index 000000000000..f71f71e22f60 --- /dev/null +++ b/models/migrations/v152.go @@ -0,0 +1,14 @@ +// Copyright 2020 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 migrations + +import "xorm.io/xorm" + +func addTrustModelToRepository(x *xorm.Engine) error { + type Repository struct { + TrustModel int + } + return x.Sync2(new(Repository)) +}