diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 38bb1305fb..54e9aed207 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -11,6 +11,7 @@ import ( "strings" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" @@ -18,6 +19,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" @@ -192,6 +194,7 @@ func Releases(ctx *context.Context) { } ctx.Data["Releases"] = releases + addVerifyTagToContext(ctx) numReleases := ctx.Data["NumReleases"].(int64) pager := context.NewPagination(int(numReleases), listOptions.PageSize, listOptions.Page, 5) @@ -201,6 +204,44 @@ func Releases(ctx *context.Context) { ctx.HTML(http.StatusOK, tplReleasesList) } +func verifyTagSignature(ctx *context.Context, r *repo_model.Release) (*asymkey.ObjectVerification, error) { + if err := r.LoadAttributes(ctx); err != nil { + return nil, err + } + gitRepo, err := gitrepo.OpenRepository(ctx, r.Repo) + if err != nil { + return nil, err + } + defer gitRepo.Close() + + tag, err := gitRepo.GetTag(r.TagName) + if err != nil { + return nil, err + } + if tag.Signature == nil { + return nil, nil + } + + verification := asymkey.ParseTagWithSignature(ctx, gitRepo, tag) + return verification, nil +} + +func addVerifyTagToContext(ctx *context.Context) { + ctx.Data["VerifyTag"] = func(r *repo_model.Release) *asymkey.ObjectVerification { + v, err := verifyTagSignature(ctx, r) + if err != nil { + return nil + } + return v + } + ctx.Data["HasSignature"] = func(verification *asymkey.ObjectVerification) bool { + if verification == nil { + return false + } + return verification.Reason != "gpg.error.not_signed_commit" + } +} + // TagsList render tags list page func TagsList(ctx *context.Context) { ctx.Data["PageIsTagList"] = true @@ -240,6 +281,7 @@ func TagsList(ctx *context.Context) { } ctx.Data["Releases"] = releases + addVerifyTagToContext(ctx) numTags := ctx.Data["NumTags"].(int64) pager := context.NewPagination(int(numTags), opts.PageSize, opts.Page, 5) @@ -304,6 +346,7 @@ func SingleRelease(ctx *context.Context) { if release.IsTag && release.Title == "" { release.Title = release.TagName } + addVerifyTagToContext(ctx) ctx.Data["PageIsSingleTag"] = release.IsTag if release.IsTag { diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl index 5aa4e51f3b..f3b4bc8443 100644 --- a/templates/repo/release/list.tmpl +++ b/templates/repo/release/list.tmpl @@ -60,6 +60,7 @@
{{$release.RenderedNote}}
+ {{template "repo/tag/verification_line" (dict "ctxData" $ "release" $release)}}
diff --git a/templates/repo/tag/list.tmpl b/templates/repo/tag/list.tmpl index 5378a8a322..82f3dc04a9 100644 --- a/templates/repo/tag/list.tmpl +++ b/templates/repo/tag/list.tmpl @@ -16,12 +16,13 @@ {{range $idx, $release := .Releases}} -

+

{{if $canReadReleases}} {{.TagName}} {{else}} {{.TagName}} {{end}} + {{template "repo/tag/verification_box" (dict "ctxData" $ "release" $release)}}

{{if $.Permission.CanRead $.UnitTypeCode}} diff --git a/templates/repo/tag/verification_box.tmpl b/templates/repo/tag/verification_box.tmpl new file mode 100644 index 0000000000..3cf88ac23e --- /dev/null +++ b/templates/repo/tag/verification_box.tmpl @@ -0,0 +1,27 @@ +{{$v := call .ctxData.VerifyTag .release}} +{{if call .ctxData.HasSignature $v}} + {{$class := "isSigned"}} + {{$href := ""}} + {{if $v.Verified}} + {{$href = $v.SigningUser.HomeLink}} + {{$class = (print $class " isVerified")}} + {{else}} + {{$class = (print $class " isWarning")}} + {{end}} + + + {{if $v.Verified}} +
+ {{if ne $v.SigningUser.ID 0}} + {{svg "gitea-lock"}} + {{ctx.AvatarUtils.Avatar $v.SigningUser 28 "signature"}} + {{else}} + {{svg "gitea-lock-cog"}} + {{ctx.AvatarUtils.AvatarByEmail $v.Verification.SigningEmail "" 28 "signature"}} + {{end}} +
+ {{else}} + {{svg "gitea-unlock"}} + {{end}} +
+{{end}} diff --git a/templates/repo/tag/verification_line.tmpl b/templates/repo/tag/verification_line.tmpl new file mode 100644 index 0000000000..f83719de23 --- /dev/null +++ b/templates/repo/tag/verification_line.tmpl @@ -0,0 +1,80 @@ +{{$v := call .ctxData.VerifyTag .release}} +{{if call .ctxData.HasSignature $v}} + {{$class := "isSigned"}} + {{$href := ""}} + {{if $v.Verified}} + {{$href = $v.SigningUser.HomeLink}} + {{$class = (print $class " isVerified")}} + {{else}} + {{$class = (print $class " isWarning")}} + {{end}} + +
+
+ {{if $v.Verified}} + {{if ne $v.SigningUser.ID 0}} + {{svg "gitea-lock" 16 "tw-mr-2"}} + {{ctx.Locale.Tr "repo.commits.signed_by"}} + {{ctx.AvatarUtils.Avatar $v.SigningUser 28 "tw-mr-2"}} + {{$v.SigningUser.GetDisplayName}} + {{else}} + {{svg "gitea-lock-cog" 16 "tw-mr-2"}} + {{ctx.Locale.Tr "repo.commits.signed_by"}}: + {{ctx.AvatarUtils.AvatarByEmail $v.SigningEmail "" 28 "tw-mr-2"}} + {{$v.SigningUser.GetDisplayName}} + {{end}} + {{else}} + {{svg "gitea-unlock" 16 "tw-mr-2"}} + {{ctx.Locale.Tr $v.Reason}} + {{end}} +
+ +
+ {{if $v.Verified}} + {{if ne $v.SigningUser.ID 0}} + {{svg "octicon-verified" 16 "tw-mr-2"}} + {{if $v.SigningSSHKey}} + {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}: + {{$v.SigningSSHKey.Fingerprint}} + {{else}} + {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}: + {{$v.SigningKey.PaddedKeyID}} + {{end}} + {{else}} + {{svg "octicon-unverified" 16 "tw-mr-2"}} + {{if $v.SigningSSHKey}} + {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}: + {{$v.SigningSSHKey.Fingerprint}} + {{else}} + {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}: + {{$v.SigningKey.PaddedKeyID}} + {{end}} + {{end}} + {{else if $v.Warning}} + {{svg "octicon-unverified" 16 "tw-mr-2"}} + {{if $v.SigningSSHKey}} + {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}: + {{$v.SigningSSHKey.Fingerprint}} + {{else}} + {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}: + {{$v.SigningKey.PaddedKeyID}} + {{end}} + {{else}} + {{if $v.SigningKey}} + {{if ne $v.SigningKey.KeyID ""}} + {{svg "octicon-verified" 16 "tw-mr-2"}} + {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}: + {{$v.SigningKey.PaddedKeyID}} + {{end}} + {{end}} + {{if $v.SigningSSHKey}} + {{if ne $v.SigningSSHKey.Fingerprint ""}} + {{svg "octicon-verified" 16 "tw-mr-2"}} + {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}: + {{$v.SigningSSHKey.Fingerprint}} + {{end}} + {{end}} + {{end}} +
+
+{{end}} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index d28dc4b96d..de18100d30 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -1874,6 +1874,31 @@ border-bottom: 1px solid var(--color-warning-border); } +.repository .release-tag-name .ui.label.isSigned, +.repository .release-list-title .ui.label.isSigned { + padding: 0 0.5em; + box-shadow: none; +} + +.repository .release-tag-name .ui.label.isSigned .avatar, +.repository .release-list-title .ui.label.isSigned .avatar { + margin-left: .5rem; +} + +.repository .release-tag-name .ui.label.isSigned.isVerified, +.repository .release-list-title .ui.label.isSigned.isVerified { + border: 1px solid var(--color-success-border); + background-color: var(--color-success-bg); + color: var(--color-success-text); +} + +.repository .release-tag-name .ui.label.isSigned.isWarning, +.repository .release-list-title .ui.label.isSigned.isWarning { + border: 1px solid var(--color-warning-border); + background-color: var(--color-warning-bg); + color: var(--color-warning-text); +} + .repository .segment.reactions.dropdown .menu, .repository .select-reaction.dropdown .menu { right: 0 !important; @@ -2107,12 +2132,19 @@ padding-top: 15px; } -.commit-header-row { +.commit-header-row, +.tag-signature-row { min-height: 50px !important; padding-top: 0 !important; padding-bottom: 0 !important; } +.tag-signature-row div { + margin-top: auto !important; + margin-bottom: auto !important; + display: inline-block !important; +} + .settings.webhooks .list > .item:not(:first-child), .settings.githooks .list > .item:not(:first-child), .settings.actions .list > .item:not(:first-child) {