mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 03:18:24 +00:00 
			
		
		
		
	Fix SSH signing key path will be displayed in the pull request UI (#35381)
Closes #35361 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		| @@ -25,7 +25,7 @@ type CommitVerification struct { | |||||||
| 	SigningUser    *user_model.User // if Verified, then SigningUser is non-nil | 	SigningUser    *user_model.User // if Verified, then SigningUser is non-nil | ||||||
| 	CommittingUser *user_model.User // if Verified, then CommittingUser is non-nil | 	CommittingUser *user_model.User // if Verified, then CommittingUser is non-nil | ||||||
| 	SigningEmail   string | 	SigningEmail   string | ||||||
| 	SigningKey     *GPGKey | 	SigningKey     *GPGKey // FIXME: need to refactor it to a new name like "SigningGPGKey", it is also used in some templates | ||||||
| 	SigningSSHKey  *PublicKey | 	SigningSSHKey  *PublicKey | ||||||
| 	TrustStatus    string | 	TrustStatus    string | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										37
									
								
								models/asymkey/key_display.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								models/asymkey/key_display.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | // Copyright 2025 The Gitea Authors. All rights reserved. | ||||||
|  | // SPDX-License-Identifier: MIT | ||||||
|  |  | ||||||
|  | package asymkey | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"os" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/modules/git" | ||||||
|  | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func GetDisplaySigningKey(key *git.SigningKey) string { | ||||||
|  | 	if key == nil || key.Format == "" { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch key.Format { | ||||||
|  | 	case git.SigningKeyFormatOpenPGP: | ||||||
|  | 		return key.KeyID | ||||||
|  | 	case git.SigningKeyFormatSSH: | ||||||
|  | 		content, err := os.ReadFile(key.KeyID) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Error("Unable to read SSH key %s: %v", key.KeyID, err) | ||||||
|  | 			return "(Unable to read SSH key)" | ||||||
|  | 		} | ||||||
|  | 		display, err := CalcFingerprint(string(content)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Error("Unable to calculate fingerprint for SSH key %s: %v", key.KeyID, err) | ||||||
|  | 			return "(Unable to calculate fingerprint for SSH key)" | ||||||
|  | 		} | ||||||
|  | 		return display | ||||||
|  | 	} | ||||||
|  | 	setting.PanicInDevOrTesting("Unknown signing key format: %s", key.Format) | ||||||
|  | 	return "(Unknown key format)" | ||||||
|  | } | ||||||
| @@ -3,13 +3,24 @@ | |||||||
|  |  | ||||||
| package git | package git | ||||||
|  |  | ||||||
|  | import "code.gitea.io/gitea/modules/setting" | ||||||
|  |  | ||||||
| // Based on https://git-scm.com/docs/git-config#Documentation/git-config.txt-gpgformat | // Based on https://git-scm.com/docs/git-config#Documentation/git-config.txt-gpgformat | ||||||
| const ( | const ( | ||||||
| 	SigningKeyFormatOpenPGP = "openpgp" // for GPG keys, the expected default of git cli | 	SigningKeyFormatOpenPGP = "openpgp" // for GPG keys, the expected default of git cli | ||||||
| 	SigningKeyFormatSSH     = "ssh" | 	SigningKeyFormatSSH     = "ssh" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // SigningKey represents an instance key info which will be used to sign git commits. | ||||||
|  | // FIXME: need to refactor it to a new name, this name conflicts with the variable names for "asymkey.GPGKey" in many places. | ||||||
| type SigningKey struct { | type SigningKey struct { | ||||||
| 	KeyID  string | 	KeyID  string | ||||||
| 	Format string | 	Format string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (s *SigningKey) String() string { | ||||||
|  | 	// Do not expose KeyID | ||||||
|  | 	// In case the key is a file path and the struct is rendered in a template, then the server path will be exposed. | ||||||
|  | 	setting.PanicInDevOrTesting("don't call SigningKey.String() - it exposes the KeyID which might be a local file path") | ||||||
|  | 	return "SigningKey:" + s.Format | ||||||
|  | } | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ import ( | |||||||
| 	"strconv" | 	"strconv" | ||||||
|  |  | ||||||
| 	activities_model "code.gitea.io/gitea/models/activities" | 	activities_model "code.gitea.io/gitea/models/activities" | ||||||
|  | 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	git_model "code.gitea.io/gitea/models/git" | 	git_model "code.gitea.io/gitea/models/git" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| @@ -494,7 +495,7 @@ func preparePullViewSigning(ctx *context.Context, issue *issues_model.Issue) { | |||||||
| 	if ctx.Doer != nil { | 	if ctx.Doer != nil { | ||||||
| 		sign, key, _, err := asymkey_service.SignMerge(ctx, pull, ctx.Doer, pull.BaseRepo.RepoPath(), pull.BaseBranch, pull.GetGitHeadRefName()) | 		sign, key, _, err := asymkey_service.SignMerge(ctx, pull, ctx.Doer, pull.BaseRepo.RepoPath(), pull.BaseBranch, pull.GetGitHeadRefName()) | ||||||
| 		ctx.Data["WillSign"] = sign | 		ctx.Data["WillSign"] = sign | ||||||
| 		ctx.Data["SigningKey"] = key | 		ctx.Data["SigningKeyMergeDisplay"] = asymkey_model.GetDisplaySigningKey(key) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			if asymkey_service.IsErrWontSign(err) { | 			if asymkey_service.IsErrWontSign(err) { | ||||||
| 				ctx.Data["WontSignReason"] = err.(*asymkey_service.ErrWontSign).Reason | 				ctx.Data["WontSignReason"] = err.(*asymkey_service.ErrWontSign).Reason | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ import ( | |||||||
| 	"path" | 	"path" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
|  | 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	git_model "code.gitea.io/gitea/models/git" | 	git_model "code.gitea.io/gitea/models/git" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| @@ -99,7 +100,7 @@ type CommitFormOptions struct { | |||||||
| 	UserCanPush              bool | 	UserCanPush              bool | ||||||
| 	RequireSigned            bool | 	RequireSigned            bool | ||||||
| 	WillSign                 bool | 	WillSign                 bool | ||||||
| 	SigningKey               *git.SigningKey | 	SigningKeyFormDisplay    string | ||||||
| 	WontSignReason           string | 	WontSignReason           string | ||||||
| 	CanCreatePullRequest     bool | 	CanCreatePullRequest     bool | ||||||
| 	CanCreateBasePullRequest bool | 	CanCreateBasePullRequest bool | ||||||
| @@ -139,7 +140,7 @@ func PrepareCommitFormOptions(ctx *Context, doer *user_model.User, targetRepo *r | |||||||
| 		protectionRequireSigned = protectedBranch.RequireSignedCommits | 		protectionRequireSigned = protectedBranch.RequireSignedCommits | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	willSign, signKeyID, _, err := asymkey_service.SignCRUDAction(ctx, targetRepo.RepoPath(), doer, targetRepo.RepoPath(), refName.String()) | 	willSign, signKey, _, err := asymkey_service.SignCRUDAction(ctx, targetRepo.RepoPath(), doer, targetRepo.RepoPath(), refName.String()) | ||||||
| 	wontSignReason := "" | 	wontSignReason := "" | ||||||
| 	if asymkey_service.IsErrWontSign(err) { | 	if asymkey_service.IsErrWontSign(err) { | ||||||
| 		wontSignReason = string(err.(*asymkey_service.ErrWontSign).Reason) | 		wontSignReason = string(err.(*asymkey_service.ErrWontSign).Reason) | ||||||
| @@ -156,14 +157,14 @@ func PrepareCommitFormOptions(ctx *Context, doer *user_model.User, targetRepo *r | |||||||
| 	canCreatePullRequest := targetRepo.UnitEnabled(ctx, unit_model.TypePullRequests) || canCreateBasePullRequest | 	canCreatePullRequest := targetRepo.UnitEnabled(ctx, unit_model.TypePullRequests) || canCreateBasePullRequest | ||||||
|  |  | ||||||
| 	opts := &CommitFormOptions{ | 	opts := &CommitFormOptions{ | ||||||
| 		TargetRepo:        targetRepo, | 		TargetRepo:            targetRepo, | ||||||
| 		WillSubmitToFork:  submitToForkedRepo, | 		WillSubmitToFork:      submitToForkedRepo, | ||||||
| 		CanCommitToBranch: canCommitToBranch, | 		CanCommitToBranch:     canCommitToBranch, | ||||||
| 		UserCanPush:       canPushWithProtection, | 		UserCanPush:           canPushWithProtection, | ||||||
| 		RequireSigned:     protectionRequireSigned, | 		RequireSigned:         protectionRequireSigned, | ||||||
| 		WillSign:          willSign, | 		WillSign:              willSign, | ||||||
| 		SigningKey:        signKeyID, | 		SigningKeyFormDisplay: asymkey_model.GetDisplaySigningKey(signKey), | ||||||
| 		WontSignReason:    wontSignReason, | 		WontSignReason:        wontSignReason, | ||||||
|  |  | ||||||
| 		CanCreatePullRequest:     canCreatePullRequest, | 		CanCreatePullRequest:     canCreatePullRequest, | ||||||
| 		CanCreateBasePullRequest: canCreateBasePullRequest, | 		CanCreateBasePullRequest: canCreateBasePullRequest, | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ so this template should be kept as small as possbile, DO NOT put large component | |||||||
| */}} | */}} | ||||||
| {{- $commit := $.Commit -}} | {{- $commit := $.Commit -}} | ||||||
| {{- $commitBaseLink := $.CommitBaseLink -}} | {{- $commitBaseLink := $.CommitBaseLink -}} | ||||||
| {{- $verification := $.CommitSignVerification -}} | {{- $verification := $.CommitSignVerification -}}{{- /* asymkey.CommitVerification */ -}} | ||||||
|  |  | ||||||
| {{- $extraClass := "" -}} | {{- $extraClass := "" -}} | ||||||
| {{- $verified := false -}} | {{- $verified := false -}} | ||||||
| @@ -50,7 +50,7 @@ so this template should be kept as small as possbile, DO NOT put large component | |||||||
|  |  | ||||||
| 	{{- if $verification.SigningSSHKey -}} | 	{{- if $verification.SigningSSHKey -}} | ||||||
| 		{{- $msgSigningKey = print (ctx.Locale.Tr "repo.commits.ssh_key_fingerprint") ": " $verification.SigningSSHKey.Fingerprint -}} | 		{{- $msgSigningKey = print (ctx.Locale.Tr "repo.commits.ssh_key_fingerprint") ": " $verification.SigningSSHKey.Fingerprint -}} | ||||||
| 	{{- else if $verification.SigningKey -}} | 	{{- else if $verification.SigningKey -}}{{- /* asymkey.GPGKey */ -}} | ||||||
| 		{{- $msgSigningKey = print (ctx.Locale.Tr "repo.commits.gpg_key_id") ": " $verification.SigningKey.PaddedKeyID -}} | 		{{- $msgSigningKey = print (ctx.Locale.Tr "repo.commits.gpg_key_id") ": " $verification.SigningKey.PaddedKeyID -}} | ||||||
| 	{{- end -}} | 	{{- end -}} | ||||||
| {{- end -}} | {{- end -}} | ||||||
|   | |||||||
| @@ -1,13 +1,15 @@ | |||||||
| <div class="commit-form-wrapper"> | <div class="commit-form-wrapper"> | ||||||
| 	{{ctx.AvatarUtils.Avatar .SignedUser 40 "commit-avatar"}} | 	{{ctx.AvatarUtils.Avatar .SignedUser 40 "commit-avatar"}} | ||||||
| 	<div class="commit-form avatar-content-left-arrow"> | 	<div class="commit-form avatar-content-left-arrow"> | ||||||
| 		<h3>{{- if .CommitFormOptions.WillSign}} | 		<h3> | ||||||
| 			<span title="{{ctx.Locale.Tr "repo.signing.will_sign" .CommitFormOptions.SigningKey}}">{{svg "octicon-lock" 24}}</span> | 		{{- if .CommitFormOptions.WillSign}} | ||||||
|  | 			<span data-tooltip-content="{{ctx.Locale.Tr "repo.signing.will_sign" .CommitFormOptions.SigningKeyFormDisplay}}">{{svg "octicon-lock" 24}}</span> | ||||||
| 			{{ctx.Locale.Tr "repo.editor.commit_signed_changes"}} | 			{{ctx.Locale.Tr "repo.editor.commit_signed_changes"}} | ||||||
| 		{{- else}} | 		{{- else}} | ||||||
| 			<span title="{{ctx.Locale.Tr (printf "repo.signing.wont_sign.%s" .CommitFormOptions.WontSignReason)}}">{{svg "octicon-unlock" 24}}</span> | 			<span title="{{ctx.Locale.Tr (printf "repo.signing.wont_sign.%s" .CommitFormOptions.WontSignReason)}}">{{svg "octicon-unlock" 24}}</span> | ||||||
| 			{{ctx.Locale.Tr "repo.editor.commit_changes"}} | 			{{ctx.Locale.Tr "repo.editor.commit_changes"}} | ||||||
| 		{{- end}}</h3> | 		{{- end}} | ||||||
|  | 		</h3> | ||||||
| 		<div class="field"> | 		<div class="field"> | ||||||
| 			<input name="commit_summary" maxlength="100" placeholder="{{if .PageIsDelete}}{{ctx.Locale.Tr "repo.editor.delete" .TreePath}}{{else if .PageIsUpload}}{{ctx.Locale.Tr "repo.editor.upload_files_to_dir" .TreePath}}{{else if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.add_tmpl"}}{{else if .PageIsPatch}}{{ctx.Locale.Tr "repo.editor.patch"}}{{else}}{{ctx.Locale.Tr "repo.editor.update" .TreePath}}{{end}}" value="{{.commit_summary}}" autofocus> | 			<input name="commit_summary" maxlength="100" placeholder="{{if .PageIsDelete}}{{ctx.Locale.Tr "repo.editor.delete" .TreePath}}{{else if .PageIsUpload}}{{ctx.Locale.Tr "repo.editor.upload_files_to_dir" .TreePath}}{{else if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.add_tmpl"}}{{else if .PageIsPatch}}{{ctx.Locale.Tr "repo.editor.patch"}}{{else}}{{ctx.Locale.Tr "repo.editor.update" .TreePath}}{{end}}" value="{{.commit_summary}}" autofocus> | ||||||
| 		</div> | 		</div> | ||||||
|   | |||||||
| @@ -188,7 +188,7 @@ | |||||||
| 					{{if .WillSign}} | 					{{if .WillSign}} | ||||||
| 						<div class="item"> | 						<div class="item"> | ||||||
| 							{{svg "octicon-lock" 16 "text green"}} | 							{{svg "octicon-lock" 16 "text green"}} | ||||||
| 							{{ctx.Locale.Tr "repo.signing.will_sign" .SigningKey}} | 							{{ctx.Locale.Tr "repo.signing.will_sign" .SigningKeyMergeDisplay}} | ||||||
| 						</div> | 						</div> | ||||||
| 					{{else if .IsSigned}} | 					{{else if .IsSigned}} | ||||||
| 						<div class="item"> | 						<div class="item"> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user