]> source.dussan.org Git - gitea.git/commitdiff
Show Signer in commit lists and add basic trust (#10425) (#10524)
authorzeripath <art27@cantab.net>
Fri, 28 Feb 2020 17:18:02 +0000 (17:18 +0000)
committerGitHub <noreply@github.com>
Fri, 28 Feb 2020 17:18:02 +0000 (14:18 -0300)
Backport #10425
Backport #10511

* Show Signer in commit lists and add basic trust (#10425)

Show the avatar of the signer in the commit list pages as we do not
enforce that the signer is an author or committer. This makes it
clearer who has signed the commit.

Also display commits signed by non-members differently from
members and in particular make it clear when a non-member signer
is different from the committer to help reduce the risk of
spoofing.

Signed-off-by: Andrew Thornton <art27@cantab.net>
Fix the signing icon in the  view_list.tmpl page (#10511)

Co-Authored-By: silverwind <me@silverwind.io>
Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
15 files changed:
docs/content/doc/features/comparison.en-us.md
models/gpg_key.go
models/repo_collaboration.go
options/locale/locale_en-US.ini
routers/repo/commit.go
routers/repo/compare.go
routers/repo/pull.go
routers/repo/view.go
routers/repo/wiki.go
templates/repo/commit_page.tmpl
templates/repo/commits_list.tmpl
templates/repo/view_list.tmpl
web_src/less/_base.less
web_src/less/_repository.less
web_src/less/themes/arc-green.less

index a5766f96fc01518b334c998813c2533508e5244c..6d5b66a954a240b6e72c851629139adbb325bf7c 100644 (file)
@@ -60,7 +60,7 @@ _Symbols used in table:_
 | Git LFS 2.0 | ✓ | ✘ | ✓ | ✓ | ✓ | ⁄ | ✓ |
 | Group Milestones | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
 | Granular user roles (Code, Issues, Wiki etc) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
-| Verified Committer | â\9c\98 | ✘ | ? | ✓ | ✓ | ✓ | ✘ |
+| Verified Committer | â\81\84 | ✘ | ? | ✓ | ✓ | ✓ | ✘ |
 | GPG Signed Commits | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
 | Reject unsigned commits | [✘](https://github.com/go-gitea/gitea/issues/2770) | ✘ | ✓ | ✓ | ✓ | ✘ | ✓ |
 | Repository Activity page | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
index 58eaa61e223e1ad65d725583ac7801e183ca05fe..7b747b31184a5abf1e88b85093497806d4600569 100644 (file)
@@ -369,6 +369,7 @@ type CommitVerification struct {
        CommittingUser *User
        SigningEmail   string
        SigningKey     *GPGKey
+       TrustStatus    string
 }
 
 // SignCommit represents a commit with validation of signature.
@@ -754,18 +755,54 @@ func verifyWithGPGSettings(gpgSettings *git.GPGSettings, sig *packet.Signature,
 }
 
 // ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys.
-func ParseCommitsWithSignature(oldCommits *list.List) *list.List {
+func ParseCommitsWithSignature(oldCommits *list.List, repository *Repository) *list.List {
        var (
                newCommits = list.New()
                e          = oldCommits.Front()
        )
+       memberMap := map[int64]bool{}
+
        for e != nil {
                c := e.Value.(UserCommit)
-               newCommits.PushBack(SignCommit{
+               signCommit := SignCommit{
                        UserCommit:   &c,
                        Verification: ParseCommitWithSignature(c.Commit),
-               })
+               }
+
+               _ = CalculateTrustStatus(signCommit.Verification, repository, &memberMap)
+
+               newCommits.PushBack(signCommit)
                e = e.Next()
        }
        return newCommits
 }
+
+// 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 {
+               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"
+                               }
+                       }
+               }
+       }
+       return
+}
index f04507f3e830c8d228205667dc8f34ab450b59a0..f421a68c7f2c5fa03bd8c26cf7e48cc9373d04d1 100644 (file)
@@ -202,3 +202,23 @@ func (repo *Repository) getRepoTeams(e Engine) (teams []*Team, err error) {
 func (repo *Repository) GetRepoTeams() ([]*Team, error) {
        return repo.getRepoTeams(x)
 }
+
+// IsOwnerMemberCollaborator checks if a provided user is the owner, a collaborator or a member of a team in a repository
+func (repo *Repository) IsOwnerMemberCollaborator(userID int64) (bool, error) {
+       if repo.OwnerID == userID {
+               return true, nil
+       }
+       teamMember, err := x.Join("INNER", "team_repo", "team_repo.team_id = team_user.team_id").
+               Join("INNER", "team_unit", "team_unit.team_id = team_user.team_id").
+               Where("team_repo.repo_id = ?", repo.ID).
+               And("team_unit.`type` = ?", UnitTypeCode).
+               And("team_user.uid = ?", userID).Table("team_user").Exist(&TeamUser{})
+       if err != nil {
+               return false, err
+       }
+       if teamMember {
+               return true, nil
+       }
+
+       return x.Get(&Collaboration{RepoID: repo.ID, UserID: userID})
+}
index 79dd188f28d4f288e61da434ec1281f82978c362..f26f38ba33e86bbc9f1441e8ddeb20597aaf583a 100644 (file)
@@ -797,6 +797,8 @@ commits.date = Date
 commits.older = Older
 commits.newer = Newer
 commits.signed_by = Signed by
+commits.signed_by_untrusted_user = Signed by untrusted user
+commits.signed_by_untrusted_user_unmatched = Signed by untrusted user who does not match committer
 commits.gpg_key_id = GPG Key ID
 
 ext_issues = Ext. Issues
index 6a4b405e0e8d561b11d1e88813fece5f9564056a..a2695d2d4d7a3b5f76cb6b56e1079e1e3fa17772 100644 (file)
@@ -65,7 +65,7 @@ func Commits(ctx *context.Context) {
                return
        }
        commits = models.ValidateCommitsWithEmails(commits)
-       commits = models.ParseCommitsWithSignature(commits)
+       commits = models.ParseCommitsWithSignature(commits, ctx.Repo.Repository)
        commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
        ctx.Data["Commits"] = commits
 
@@ -134,7 +134,7 @@ func SearchCommits(ctx *context.Context) {
                return
        }
        commits = models.ValidateCommitsWithEmails(commits)
-       commits = models.ParseCommitsWithSignature(commits)
+       commits = models.ParseCommitsWithSignature(commits, ctx.Repo.Repository)
        commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
        ctx.Data["Commits"] = commits
 
@@ -180,7 +180,7 @@ func FileHistory(ctx *context.Context) {
                return
        }
        commits = models.ValidateCommitsWithEmails(commits)
-       commits = models.ParseCommitsWithSignature(commits)
+       commits = models.ParseCommitsWithSignature(commits, ctx.Repo.Repository)
        commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
        ctx.Data["Commits"] = commits
 
@@ -262,12 +262,18 @@ func Diff(ctx *context.Context) {
        setPathsCompareContext(ctx, parentCommit, commit, headTarget)
        ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitID)
        ctx.Data["Commit"] = commit
-       ctx.Data["Verification"] = models.ParseCommitWithSignature(commit)
+       verification := models.ParseCommitWithSignature(commit)
+       ctx.Data["Verification"] = verification
        ctx.Data["Author"] = models.ValidateCommitWithEmail(commit)
        ctx.Data["Diff"] = diff
        ctx.Data["Parents"] = parents
        ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
 
+       if err := models.CalculateTrustStatus(verification, ctx.Repo.Repository, nil); err != nil {
+               ctx.ServerError("CalculateTrustStatus", err)
+               return
+       }
+
        note := &git.Note{}
        err = git.GetNote(ctx.Repo.GitRepo, commitID, note)
        if err == nil {
index 3fa86a8d9f695162a91b53c6494056c354bf2de9..92a666891eaa1905512412cc68b41b2fa7ac0acb 100644 (file)
@@ -316,7 +316,7 @@ func PrepareCompareDiff(
        }
 
        compareInfo.Commits = models.ValidateCommitsWithEmails(compareInfo.Commits)
-       compareInfo.Commits = models.ParseCommitsWithSignature(compareInfo.Commits)
+       compareInfo.Commits = models.ParseCommitsWithSignature(compareInfo.Commits, headRepo)
        compareInfo.Commits = models.ParseCommitsWithStatus(compareInfo.Commits, headRepo)
        ctx.Data["Commits"] = compareInfo.Commits
        ctx.Data["CommitCount"] = compareInfo.Commits.Len()
index 8167a53209222e0bc87425a448260b8400523a05..c8c35ae917f3901cd0dc4a1f4de33d3a1a0ed1b0 100644 (file)
@@ -477,7 +477,7 @@ func ViewPullCommits(ctx *context.Context) {
        ctx.Data["Reponame"] = ctx.Repo.Repository.Name
        commits = prInfo.Commits
        commits = models.ValidateCommitsWithEmails(commits)
-       commits = models.ParseCommitsWithSignature(commits)
+       commits = models.ParseCommitsWithSignature(commits, ctx.Repo.Repository)
        commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
        ctx.Data["Commits"] = commits
        ctx.Data["CommitCount"] = commits.Len()
index 8730523d891e066632feaecb6ea4cfc947807462..c62a78cf48aa118eda138b479e5c25babfb062ca 100644 (file)
@@ -181,7 +181,14 @@ func renderDirectory(ctx *context.Context, treeLink string) {
        // Show latest commit info of repository in table header,
        // or of directory if not in root directory.
        ctx.Data["LatestCommit"] = latestCommit
-       ctx.Data["LatestCommitVerification"] = models.ParseCommitWithSignature(latestCommit)
+       verification := models.ParseCommitWithSignature(latestCommit)
+
+       if err := models.CalculateTrustStatus(verification, ctx.Repo.Repository, nil); err != nil {
+               ctx.ServerError("CalculateTrustStatus", err)
+               return
+       }
+       ctx.Data["LatestCommitVerification"] = verification
+
        ctx.Data["LatestCommitUser"] = models.ValidateCommitWithEmail(latestCommit)
 
        statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository, ctx.Repo.Commit.ID.String(), 0)
index f18625a59972a14d2f5d10b89bcb679a7deca621..a01498fb0a1aae6881889d299b9f44f7104d2950 100644 (file)
@@ -284,7 +284,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
                return nil, nil
        }
        commitsHistory = models.ValidateCommitsWithEmails(commitsHistory)
-       commitsHistory = models.ParseCommitsWithSignature(commitsHistory)
+       commitsHistory = models.ParseCommitsWithSignature(commitsHistory, ctx.Repo.Repository)
 
        ctx.Data["Commits"] = commitsHistory
 
index 52bedc75b777a58e19695b44f16a5ce6068ea317..a3873e4738c4246343470a1b5257b5935d6bee12 100644 (file)
@@ -2,7 +2,22 @@
 <div class="repository diff">
        {{template "repo/header" .}}
        <div class="ui container {{if .IsSplitStyle}}fluid padded{{end}}">
-               <div class="ui top attached info clearing segment {{if .Commit.Signature}} isSigned {{if .Verification.Verified }} isVerified {{end}}{{end}}">
+               {{$class := ""}}
+               {{if .Commit.Signature}}
+                       {{$class = (printf "%s%s" $class " isSigned")}}
+                       {{if .Verification.Verified}}
+                               {{if eq .Verification.TrustStatus "trusted"}}
+                                       {{$class = (printf "%s%s" $class " isVerified")}}
+                               {{else if eq .Verification.TrustStatus "untrusted"}}
+                                       {{$class = (printf "%s%s" $class " isVerifiedUntrusted")}}
+                               {{else}}
+                                       {{$class = (printf "%s%s" $class " isVerifiedUnmatched")}}
+                               {{end}}
+                       {{else if .Verification.Warning}}
+                               {{$class = (printf "%s%s" $class " isWarning")}}
+                       {{end}}
+               {{end}}
+               <div class="ui top attached info clearing segment {{$class}}">
                        <a class="ui floated right blue tiny button" href="{{EscapePound .SourcePath}}">
                                {{.i18n.Tr "repo.diff.browse_source"}}
                        </a>
                        {{end}}
                        <span class="text grey"><i class="octicon octicon-git-branch"></i>{{.BranchName}}</span>
                </div>
-               <div class="ui attached info segment {{if .Commit.Signature}} isSigned {{if .Verification.Verified }} isVerified {{end}}{{end}}">
+               <div class="ui attached info segment {{$class}}">
                        <div class="ui stackable grid">
                                <div class="nine wide column">
                                        {{if .Author}}
                                                <img class="ui avatar image" src="{{.Author.RelAvatarLink}}" />
                                        {{if .Author.FullName}}
-                                       <a href="{{.Author.HomeLink}}"><strong>{{.Author.FullName}}</strong></a> {{if .IsSigned}}<{{.Commit.Author.Email}}>{{end}}
+                                       <a href="{{.Author.HomeLink}}"><strong>{{.Author.FullName}}</strong> {{if .IsSigned}}&lt;{{.Commit.Author.Email}}&gt;{{end}}</a>
                                        {{else}}
-                                       <a href="{{.Author.HomeLink}}"><strong>{{.Commit.Author.Name}}</strong></a> {{if .IsSigned}}<{{.Commit.Author.Email}}>{{end}}
+                                       <a href="{{.Author.HomeLink}}"><strong>{{.Commit.Author.Name}}</strong> {{if .IsSigned}}&lt;{{.Commit.Author.Email}}&gt;{{end}}</a>
                                        {{end}}
                                        {{else}}
                                                <img class="ui avatar image" src="{{AvatarLink .Commit.Author.Email}}" />
@@ -30,7 +45,7 @@
                                                <span> </span>
                                                {{if ne .Verification.CommittingUser.ID 0}}
                                                        <img class="ui avatar image" src="{{.Verification.CommittingUser.RelAvatarLink}}" />
-                                                       <a href="{{.Verification.CommittingUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong></a> <{{.Commit.Committer.Email}}>
+                                                       <a href="{{.Verification.CommittingUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong> &lt;{{.Commit.Committer.Email}}&gt;</a> 
                                                {{else}}
                                                        <img class="ui avatar image" src="{{AvatarLink .Commit.Committer.Email}}" />
                                                        <strong>{{.Commit.Committer.Name}}</strong>
                        </div><!-- end grid -->
                </div>
                {{if .Commit.Signature}}
-                       {{if .Verification.Verified }}
-                               <div class="ui bottom attached positive message">
+                       <div class="ui bottom attached message {{$class}}">
+                               {{if .Verification.Verified }}
                                        {{if ne .Verification.SigningUser.ID 0}}
-                                               <i class="green lock icon"></i>
-                                               <span>{{.i18n.Tr "repo.commits.signed_by"}}:</span>
+                                               <i class="lock icon"></i>
+                                               {{if eq .Verification.TrustStatus "trusted"}}
+                                                       <span class="ui text">{{.i18n.Tr "repo.commits.signed_by"}}:</span>
+                                               {{else if eq .Verification.TrustStatus "untrusted"}}
+                                                       <span class="ui text">{{.i18n.Tr "repo.commits.signed_by_untrusted_user"}}:</span>
+                                               {{else}}
+                                                       <span class="ui text">{{.i18n.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}:</span>
+                                               {{end}}
                                                <img class="ui avatar image" src="{{.Verification.SigningUser.RelAvatarLink}}" />
-                                               <a href="{{.Verification.SigningUser.HomeLink}}"><strong>{{.Verification.SigningUser.Name}}</strong></a> <{{.Verification.SigningEmail}}>
-                                               <span class="pull-right"><span>{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> {{.Verification.SigningKey.KeyID}}</span>
+                                               <a href="{{.Verification.SigningUser.HomeLink}}"><strong>{{.Verification.SigningUser.Name}}</strong> <{{.Verification.SigningEmail}}></a>
+                                               <span class="pull-right"><span class="ui text">{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> {{.Verification.SigningKey.KeyID}}</span>
                                        {{else}}
                                                <i class="icons" title="{{.i18n.Tr "gpg.default_key"}}">
-                                                       <i class="green lock icon"></i>
+                                                       <i class="lock icon"></i>
                                                        <i class="tiny inverted cog icon centerlock"></i>
                                                </i>
-                                               <span>{{.i18n.Tr "repo.commits.signed_by"}}:</span>
+                                               <span class="ui text">{{.i18n.Tr "repo.commits.signed_by"}}:</span>
                                                <img class="ui avatar image" src="{{AvatarLink .Verification.SigningEmail}}" />
                                                <strong>{{.Verification.SigningUser.Name}}</strong> <{{.Verification.SigningEmail}}>
-                                               <span class="pull-right"><span>{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> <i class="cogs icon" title="{{.i18n.Tr "gpg.default_key"}}"></i>{{.Verification.SigningKey.KeyID}}</span>
+                                               <span class="pull-right"><span class="ui text">{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> <i class="cogs icon" title="{{.i18n.Tr "gpg.default_key"}}"></i>{{.Verification.SigningKey.KeyID}}</span>
                                        {{end}}
-                               </div>
-                       {{else if .Verification.Warning}}
-                               <div class="ui bottom attached message">
-                                 <i class="red unlock icon"></i>
-                                 <span class="red text">{{.i18n.Tr .Verification.Reason}}</span>
-                                 <span class="pull-right"><span class="red text">{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> <i class="red warning icon"></i>{{.Verification.SigningKey.KeyID}}</span>
-                               </div>
-                       {{else}}
-                               <div class="ui bottom attached message">
-                                 <i class="grey unlock icon"></i>
+                               {{else if .Verification.Warning}}
+                                       <i class="unlock icon"></i>
+                                       <span class="ui text">{{.i18n.Tr .Verification.Reason}}</span>
+                                       <span class="pull-right"><span class="ui text">{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> <i class="warning icon"></i>{{.Verification.SigningKey.KeyID}}</span>
+                               {{else}}
+                                 <i class="unlock icon"></i>
                                  {{.i18n.Tr .Verification.Reason}}
                                  {{if and .Verification.SigningKey (ne .Verification.SigningKey.KeyID "")}}
-                                       <span class="pull-right"><span class="red text">{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> <i class="red warning icon"></i>{{.Verification.SigningKey.KeyID}}</span>
+                                       <span class="pull-right"><span class="ui text">{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> <i class="warning icon"></i>{{.Verification.SigningKey.KeyID}}</span>
                                  {{end}}
-                               </div>
-                       {{end}}
+                               {{end}}
+                       </div>
                {{end}}
                {{if .Note}}
                        <div class="ui top attached info segment message git-notes">
index 01096f2085dcf28fe7c5c1c786d8b446861d7231..5dc12c642bdbaa492635f19816570bbbde36dfc4 100644 (file)
                                                        {{if .Signature}}
                                                                {{$class = (printf "%s%s" $class " isSigned")}}
                                                                {{if .Verification.Verified}}
-                                                                       {{$class = (printf "%s%s" $class " isVerified")}}
+                                                                       {{if eq .Verification.TrustStatus "trusted"}}
+                                                                               {{$class = (printf "%s%s" $class " isVerified")}}
+                                                                       {{else if eq .Verification.TrustStatus "untrusted"}}
+                                                                               {{$class = (printf "%s%s" $class " isVerifiedUntrusted")}}
+                                                                       {{else}}
+                                                                               {{$class = (printf "%s%s" $class " isVerifiedUnmatched")}}
+                                                                       {{end}}
                                                                {{else if .Verification.Warning}}
                                                                        {{$class = (printf "%s%s" $class " isWarning")}}
                                                                {{end}}
                                                        {{else}}
                                                                <span class="{{$class}}">
                                                        {{end}}
-                                                               {{ShortSha .ID.String}}
+                                                               <span class="shortsha">{{ShortSha .ID.String}}</span>
                                                                {{if .Signature}}
                                                                        <div class="ui detail icon button">
                                                                                {{if .Verification.Verified}}
-                                                                                       {{if ne .Verification.SigningUser.ID 0}}
-                                                                                               <i title="{{.Verification.Reason}}" class="lock green icon"></i>
-                                                                                       {{else}}
-                                                                                               <i title="{{.Verification.Reason}}" class="icons">
-                                                                                                       <i class="green lock icon"></i>
-                                                                                                       <i class="tiny inverted cog icon centerlock"></i>
-                                                                                               </i>
-                                                                                       {{end}}
-                                                                               {{else if .Verification.Warning}}
-                                                                                       <i title="{{$.i18n.Tr .Verification.Reason}}" class="red unlock icon"></i>
+                                                                                       <div title="{{if eq .Verification.TrustStatus "trusted"}}{{else if eq .Verification.TrustStatus "untrusted"}}{{$.i18n.Tr "repo.commits.signed_by_untrusted_user"}}: {{else}}{{$.i18n.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}: {{end}}{{.Verification.Reason}}">
+                                                                                               {{if ne .Verification.SigningUser.ID 0}}
+                                                                                                       <i class="lock icon"></i>
+                                                                                                       <img class="ui signature avatar image" src="{{.Verification.SigningUser.RelAvatarLink}}" />
+                                                                                               {{else}}
+                                                                                                       <i title="{{.Verification.Reason}}" class="icons">
+                                                                                                               <i class="lock icon"></i>
+                                                                                                               <i class="tiny inverted cog icon centerlock"></i>
+                                                                                                       </i>
+                                                                                                       <img class="ui signature avatar image" src="{{AvatarLink .Verification.SigningEmail}}" />
+                                                                                               {{end}}
+                                                                                       </div>
                                                                                {{else}}
                                                                                        <i title="{{$.i18n.Tr .Verification.Reason}}" class="unlock icon"></i>
                                                                                {{end}}
index 32b1a0078abf2b30fc3bf0262c1009c511e60171..8705862a26587d201c17e69310bfb4776a073d9c 100644 (file)
                                                <strong>{{.LatestCommit.Author.Name}}</strong>
                                        {{end}}
                                {{end}}
-                               <a rel="nofollow" class="ui sha label {{if .LatestCommit.Signature}} isSigned {{if .LatestCommitVerification.Verified }} isVerified {{end}}{{end}}" href="{{.RepoLink}}/commit/{{.LatestCommit.ID}}">
-                                               {{ShortSha .LatestCommit.ID.String}}
+                               <a rel="nofollow" class="ui sha label {{if .LatestCommit.Signature}} isSigned {{if .LatestCommitVerification.Verified }} isVerified{{if eq .LatestCommitVerification.TrustStatus "trusted"}}{{else if eq .LatestCommitVerification.TrustStatus "untrusted"}}Untrusted{{else}}Unmatched{{end}}{{else if .LatestCommitVerification.Warning}} isWarning{{end}}{{end}}" href="{{.RepoLink}}/commit/{{.LatestCommit.ID}}">
+                                       <span class="shortsha">{{ShortSha .LatestCommit.ID.String}}</span>
+                                       <div class="ui detail icon button">
                                                {{if .LatestCommit.Signature}}
-                                                       <div class="ui detail icon button">
-                                                               {{if .LatestCommitVerification.Verified}}
-                                                                       <i title="{{.LatestCommitVerification.Reason}}" class="lock green icon"></i>
-                                                               {{else}}
-                                                                       <i title="{{$.i18n.Tr .LatestCommitVerification.Reason}}" class="unlock icon"></i>
-                                                               {{end}}
-                                                       </div>
+                                                       {{if .LatestCommitVerification.Verified}}
+                                                               <div title="{{if eq .LatestCommitVerification.TrustStatus "trusted"}}{{else if eq .LatestCommitVerification.TrustStatus "untrusted"}}{{.i18n.Tr "repo.commits.signed_by_untrusted_user"}}: {{else}}{{.i18n.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}: {{end}}{{.LatestCommitVerification.Reason}}">
+                                                                       {{if ne .LatestCommitVerification.SigningUser.ID 0}}
+                                                                               <i class="lock icon"></i>
+                                                                               <img class="ui signature avatar image" src="{{.LatestCommitVerification.SigningUser.RelAvatarLink}}" />
+                                                                       {{else}}
+                                                                               <i title="{{.LatestCommitVerification.Reason}}" class="icons">
+                                                                                       <i class="lock icon"></i>
+                                                                                       <i class="tiny inverted cog icon centerlock"></i>
+                                                                               </i>
+                                                                               <img class="ui signature avatar image" src="{{AvatarLink .LatestCommitVerification.SigningEmail}}" />
+                                                                       {{end}}
+                                                               </div>
+                                                       {{else}}
+                                                               <i title="{{$.i18n.Tr .LatestCommitVerification.Reason}}" class="unlock icon"></i>
+                                                       {{end}}
                                                {{end}}
+                                       </div>
                                </a>
                                {{template "repo/commit_status" .LatestCommitStatus}}
                                {{ $commitLink:= printf "%s/commit/%s" .RepoLink .LatestCommit.ID }}
index 068b9e8144c4b13e58756b9914298d6db94769e5..4806cc56a56486c1eeba6c800e6c77a0401d1850 100644 (file)
@@ -471,6 +471,10 @@ code,
             color: #fbbd08 !important;
         }
 
+        &.orange {
+            color: #f2711c !important;
+        }
+
         &.gold {
             color: #a1882b !important;
         }
@@ -668,6 +672,10 @@ code,
             background-color: #fbbf09 !important;
         }
 
+        &.orange {
+            background-color: #f2711c !important;
+        }
+
         &.gold {
             background-color: #a1882b !important;
         }
@@ -719,6 +727,10 @@ code,
             border-color: #fbbd08 !important;
         }
 
+        &.orange {
+            border-color: #f2711c !important;
+        }
+
         &.gold {
             border-color: #a1882b !important;
         }
@@ -1041,7 +1053,7 @@ i.icons .icon:first-child {
 }
 
 i.icon.centerlock {
-    top: 1.5em;
+    top: 1.45em;
 }
 
 .ui.label > .detail .icons {
index e75fb0fae7549570b4ceb5c0959f9b723e0dea9d..3fbd46708140addc5ce83e6d9fc0522567b7c8fe 100644 (file)
                     text-align: center;
                 }
 
-                width: 140px;
+                width: 175px;
             }
         }
 
     #repo-files-table .sha.label {
         border: 1px solid #bbbbbb;
 
+        .ui.signature.avatar {
+            height: 16px;
+            margin-bottom: 0;
+            width: auto;
+        }
+
         .detail.icon {
             background: #fafafa;
             margin: -6px -10px -4px 0;
-            padding: 5px 3px 5px 6px;
+            padding: 5px 4px 5px 6px;
             border-left: 1px solid #bbbbbb;
+            border-top: 0;
+            border-right: 0;
+            border-bottom: 0;
             border-top-left-radius: 0;
             border-bottom-left-radius: 0;
+
+            img {
+                margin-right: 0;
+            }
+
+            > div {
+                display: inline-flex;
+                align-items: center;
+            }
         }
 
         &.isSigned.isWarning {
             border: 1px solid #db2828;
             background: fade(#db2828, 10%);
 
+            .shortsha {
+                display: inline-block;
+                padding-top: 1px;
+            }
+
             .detail.icon {
-                border-left: 1px solid fade(#db2828, 50%);
+                border-left: 1px solid #db2828;
+                color: #db2828;
+            }
+
+            &:hover {
+                background: fade(#db2828, 30%) !important;
             }
         }
 
             border: 1px solid #21ba45;
             background: fade(#21ba45, 10%);
 
+            .shortsha {
+                display: inline-block;
+                padding-top: 1px;
+            }
+
             .detail.icon {
                 border-left: 1px solid #21ba45;
+                color: #21ba45;
             }
 
             &:hover {
                 background: fade(#21ba45, 30%) !important;
             }
         }
+
+        &.isSigned.isVerifiedUntrusted {
+            border: 1px solid #fbbd08;
+            background: fade(#fbbd08, 10%);
+
+            .shortsha {
+                display: inline-block;
+                padding-top: 1px;
+            }
+
+            .detail.icon {
+                border-left: 1px solid #fbbd08;
+                color: #fbbd08;
+            }
+
+            &:hover {
+                background: fade(#fbbd08, 30%) !important;
+            }
+        }
+
+        &.isSigned.isVerifiedUnmatched {
+            border: 1px solid #f2711c;
+            background: fade(#f2711c, 10%);
+
+            .shortsha {
+                display: inline-block;
+                padding-top: 1px;
+            }
+
+            .detail.icon {
+                border-left: 1px solid #f2711c;
+                color: #f2711c;
+            }
+
+            &:hover {
+                background: fade(#f2711c, 30%) !important;
+            }
+        }
     }
 
     .diff-detail-box {
         }
     }
 
-    .ui.attached.isSigned.isVerified {
-        &:not(.positive) {
-            border-left: 1px solid #a3c293;
-            border-right: 1px solid #a3c293;
+    .ui.attached.isSigned.isWarning {
+        border-left: 1px solid #c29393;
+        border-right: 1px solid #c29393;
+
+        &.top,
+        &.message {
+            border-top: 1px solid #c29393;
         }
 
-        &.top:not(.positive) {
+        &.message {
+            box-shadow: none;
+            background-color: #fff5f5;
+            color: #d95c5c;
+
+            .ui.text {
+                color: #d64444;
+            }
+        }
+
+        &:last-child,
+        &.bottom {
+            border-bottom: 1px solid #c29393;
+        }
+    }
+
+    .ui.attached.isSigned:not(.isWarning) .pull-right {
+        padding-top: 5px;
+    }
+
+    .ui.attached.isSigned.isVerified {
+        border-left: 1px solid #a3c293;
+        border-right: 1px solid #a3c293;
+
+        &.top,
+        &.message {
             border-top: 1px solid #a3c293;
         }
 
-        &:not(.positive):last-child {
+        &.message {
+            box-shadow: none;
+            background-color: #fcfff5;
+            color: #6cc644;
+
+            .pull-right {
+                color: #000;
+            }
+
+            .ui.text {
+                color: #21ba45;
+            }
+        }
+
+        &:last-child,
+        &.bottom {
             border-bottom: 1px solid #a3c293;
         }
     }
 
+    .ui.attached.isSigned.isVerifiedUntrusted {
+        border-left: 1px solid #c2c193;
+        border-right: 1px solid #c2c193;
+
+        &.top,
+        &.message {
+            border-top: 1px solid #c2c193;
+        }
+
+        &.message {
+            box-shadow: none;
+            background-color: #fffff5;
+            color: #fbbd08;
+
+            .ui.text {
+                color: #d2ab00;
+            }
+        }
+
+        &:last-child,
+        &.bottom {
+            border-bottom: 1px solid #c2c193;
+        }
+    }
+
+    .ui.attached.isSigned.isVerifiedUnmatched {
+        border-left: 1px solid #c2a893;
+        border-right: 1px solid #c2a893;
+
+        &.top,
+        &.message {
+            border-top: 1px solid #c2a893;
+        }
+
+        &.message {
+            box-shadow: none;
+            background-color: #fffaf5;
+            color: #f2711c;
+
+            .ui.text {
+                color: #ee5f00;
+            }
+        }
+
+        &:last-child,
+        &.bottom {
+            border-bottom: 1px solid #c2a893;
+        }
+    }
+
     .ui.segment.sub-menu {
         padding: 7px;
         line-height: 0;
index c5f4a43f596c120bbbc2411124f20543b9cf7200..1bd644ece8d2c46eb2b5fdebdfed445fb0206602 100644 (file)
@@ -1136,6 +1136,64 @@ a.ui.labels .label:hover {
     border-left-color: #888;
 }
 
+.repository .ui.attached.message.isSigned.isVerified {
+    background-color: #394829;
+    color: #9e9e9e;
+
+    &.message {
+        color: #87ab63;
+        .ui.text {
+            color: #9e9e9e;
+        }
+        .pull-right {
+            color: #87ab63;
+        }
+    }
+}
+
+.repository .ui.attached.message.isSigned.isVerifiedUntrusted {
+    background-color: #4a3903;
+    color: #9e9e9e;
+    &.message {
+        color: #c2c193;
+        .ui.text {
+            color: #9e9e9e;
+        }
+        .pull-right,
+        a {
+            color: #c2c193;
+        }
+    }
+}
+
+.repository .ui.attached.message.isSigned.isVerifiedUnmatched {
+    background-color: #4e3321;
+    color: #9e9e9e;
+    &.message {
+        color: #c2a893;
+        .ui.text {
+            color: #9e9e9e;
+        }
+        .pull-right,
+        a {
+            color: #c2a893;
+        }
+    }
+}
+
+.repository .ui.attached.message.isSigned.isWarning {
+    background-color: rgba(80, 23, 17, 0.6);
+    &.message {
+        color: #d07d7d;
+        .ui.text {
+            color: #d07d7d;
+        }
+        .pull-right {
+            color: #9e9e9e;
+        }
+    }
+}
+
 .repository .label.list .item {
     border-bottom: 1px dashed #4c505c;
 }
@@ -1146,6 +1204,11 @@ a.ui.labels .label:hover {
     color: #87ab63 !important;
 }
 
+.ui.text.yellow,
+.yellow.icon.icon.icon {
+    color: #e4ac07 !important;
+}
+
 .repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(1),
 .repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(2),
 .repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(3),