Add link to user profile in markdown mention only if user exists (#21533)

Previously mentioning a user would link to its profile, regardless of
whether the user existed. This change tests if the user exists and only
if it does - a link to its profile is added.

* Fixes #3444

Signed-off-by: Yarden Shoham <hrsi88@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
Yarden Shoham 2022-10-22 20:15:52 +03:00 committed by GitHub
parent 82ecd3b19e
commit 63ebb53fd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 103 additions and 5 deletions

View File

@ -33,6 +33,7 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers" "code.gitea.io/gitea/routers"
markup_service "code.gitea.io/gitea/services/markup"
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/config"
@ -112,7 +113,7 @@ func runPR() {
log.Printf("[PR] Setting up router\n") log.Printf("[PR] Setting up router\n")
// routers.GlobalInit() // routers.GlobalInit()
external.RegisterRenderers() external.RegisterRenderers()
markup.Init() markup.Init(markup_service.ProcessorHelper())
c := routers.NormalRoutes(graceful.GetManager().HammerContext()) c := routers.NormalRoutes(graceful.GetManager().HammerContext())
log.Printf("[PR] Ready for testing !\n") log.Printf("[PR] Ready for testing !\n")

View File

@ -603,8 +603,14 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
start = loc.End start = loc.End
continue continue
} }
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mention[1:]), mention, "mention")) mentionedUsername := mention[1:]
node = node.NextSibling.NextSibling
if processorHelper.IsUsernameMentionable != nil && processorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mentionedUsername), mention, "mention"))
node = node.NextSibling.NextSibling
} else {
node = node.NextSibling
}
start = 0 start = 0
} }
} }

View File

@ -38,6 +38,11 @@ func TestMain(m *testing.M) {
if err := git.InitSimple(context.Background()); err != nil { if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err) log.Fatal("git init failed, err: %v", err)
} }
markup.Init(&markup.ProcessorHelper{
IsUsernameMentionable: func(ctx context.Context, username string) bool {
return username == "r-lyeh"
},
})
os.Exit(m.Run()) os.Exit(m.Run())
} }

View File

@ -19,8 +19,18 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )
type ProcessorHelper struct {
IsUsernameMentionable func(ctx context.Context, username string) bool
}
var processorHelper ProcessorHelper
// Init initialize regexps for markdown parsing // Init initialize regexps for markdown parsing
func Init() { func Init(ph *ProcessorHelper) {
if ph != nil {
processorHelper = *ph
}
NewSanitizer() NewSanitizer()
if len(setting.Markdown.CustomURLSchemes) > 0 { if len(setting.Markdown.CustomURLSchemes) > 0 {
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes) CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)

View File

@ -5,6 +5,7 @@
package misc package misc
import ( import (
go_context "context"
"io" "io"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@ -13,6 +14,7 @@ import (
"testing" "testing"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/templates"
@ -50,6 +52,11 @@ func wrap(ctx *context.Context) *context.APIContext {
func TestAPI_RenderGFM(t *testing.T) { func TestAPI_RenderGFM(t *testing.T) {
setting.AppURL = AppURL setting.AppURL = AppURL
markup.Init(&markup.ProcessorHelper{
IsUsernameMentionable: func(ctx go_context.Context, username string) bool {
return username == "r-lyeh"
},
})
options := api.MarkdownOption{ options := api.MarkdownOption{
Mode: "gfm", Mode: "gfm",

View File

@ -41,6 +41,7 @@ import (
"code.gitea.io/gitea/services/automerge" "code.gitea.io/gitea/services/automerge"
"code.gitea.io/gitea/services/cron" "code.gitea.io/gitea/services/cron"
"code.gitea.io/gitea/services/mailer" "code.gitea.io/gitea/services/mailer"
markup_service "code.gitea.io/gitea/services/markup"
repo_migrations "code.gitea.io/gitea/services/migrations" repo_migrations "code.gitea.io/gitea/services/migrations"
mirror_service "code.gitea.io/gitea/services/mirror" mirror_service "code.gitea.io/gitea/services/mirror"
pull_service "code.gitea.io/gitea/services/pull" pull_service "code.gitea.io/gitea/services/pull"
@ -123,7 +124,7 @@ func GlobalInitInstalled(ctx context.Context) {
highlight.NewContext() highlight.NewContext()
external.RegisterRenderers() external.RegisterRenderers()
markup.Init() markup.Init(markup_service.ProcessorHelper())
if setting.EnableSQLite3 { if setting.EnableSQLite3 {
log.Info("SQLite3 support is enabled") log.Info("SQLite3 support is enabled")

View File

@ -0,0 +1,19 @@
// Copyright 2022 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 markup
import (
"path/filepath"
"testing"
"code.gitea.io/gitea/models/unittest"
)
func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: filepath.Join("..", ".."),
FixtureFiles: []string{"user.yml"},
})
}

View File

@ -0,0 +1,29 @@
// Copyright 2022 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 markup
import (
"context"
"code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
)
func ProcessorHelper() *markup.ProcessorHelper {
return &markup.ProcessorHelper{
IsUsernameMentionable: func(ctx context.Context, username string) bool {
// TODO: cast ctx to modules/context.Context and use IsUserVisibleToViewer
// Only link if the user actually exists
userExists, err := user.IsUserExist(ctx, 0, username)
if err != nil {
log.Error("Failed to validate user in mention %q exists, assuming it does", username)
userExists = true
}
return userExists
},
}
}

View File

@ -0,0 +1,20 @@
// Copyright 2022 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 markup
import (
"context"
"testing"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
)
func TestProcessorHelper(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
assert.True(t, ProcessorHelper().IsUsernameMentionable(context.Background(), "user10"))
assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), "no-such-user"))
}