mirror of https://github.com/go-gitea/gitea.git
Fix the permission check for user search API and limit the number of returned users for `/user/search` (#32310)
Partially backport #32288 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
parent
b7d12347f3
commit
0d11ba93dd
|
@ -356,12 +356,20 @@ func reqToken() func(ctx *context.APIContext) {
|
|||
|
||||
func reqExploreSignIn() func(ctx *context.APIContext) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if setting.Service.Explore.RequireSigninView && !ctx.IsSigned {
|
||||
if (setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView) && !ctx.IsSigned {
|
||||
ctx.Error(http.StatusUnauthorized, "reqExploreSignIn", "you must be signed in to search for users")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func reqUsersExploreEnabled() func(ctx *context.APIContext) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if setting.Service.Explore.DisableUsersPage {
|
||||
ctx.NotFound()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func reqBasicOrRevProxyAuth() func(ctx *context.APIContext) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if ctx.IsSigned && setting.Service.EnableReverseProxyAuthAPI && ctx.Data["AuthedMethod"].(string) == auth.ReverseProxyMethodName {
|
||||
|
@ -955,7 +963,7 @@ func Routes() *web.Route {
|
|||
|
||||
// Users (requires user scope)
|
||||
m.Group("/users", func() {
|
||||
m.Get("/search", reqExploreSignIn(), user.Search)
|
||||
m.Get("/search", reqExploreSignIn(), reqUsersExploreEnabled(), user.Search)
|
||||
|
||||
m.Group("/{username}", func() {
|
||||
m.Get("", reqExploreSignIn(), user.GetInfo)
|
||||
|
|
|
@ -8,37 +8,24 @@ import (
|
|||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
)
|
||||
|
||||
// Search search users
|
||||
func Search(ctx *context.Context) {
|
||||
listOptions := db.ListOptions{
|
||||
Page: ctx.FormInt("page"),
|
||||
PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")),
|
||||
}
|
||||
|
||||
users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
||||
// SearchCandidates searches candidate users for dropdown list
|
||||
func SearchCandidates(ctx *context.Context) {
|
||||
users, _, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Keyword: ctx.FormTrim("q"),
|
||||
UID: ctx.FormInt64("uid"),
|
||||
Type: user_model.UserTypeIndividual,
|
||||
IsActive: ctx.FormOptionalBool("active"),
|
||||
ListOptions: listOptions,
|
||||
IsActive: optional.Some(true),
|
||||
ListOptions: db.ListOptions{PageSize: setting.UI.MembersPagingNum},
|
||||
})
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]any{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
ctx.ServerError("Unable to search users", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(maxResults)
|
||||
|
||||
ctx.JSON(http.StatusOK, map[string]any{
|
||||
"ok": true,
|
||||
"data": convert.ToUsers(ctx, ctx.Doer, users),
|
||||
})
|
||||
ctx.JSON(http.StatusOK, map[string]any{"data": convert.ToUsers(ctx, ctx.Doer, users)})
|
||||
}
|
||||
|
|
|
@ -668,7 +668,7 @@ func registerRoutes(m *web.Route) {
|
|||
m.Post("/forgot_password", auth.ForgotPasswdPost)
|
||||
m.Post("/logout", auth.SignOut)
|
||||
m.Get("/stopwatches", reqSignIn, user.GetStopwatches)
|
||||
m.Get("/search", ignExploreSignIn, user.Search)
|
||||
m.Get("/search_candidates", ignExploreSignIn, user.SearchCandidates)
|
||||
m.Group("/oauth2", func() {
|
||||
m.Get("/{provider}", auth.SignInOAuth)
|
||||
m.Get("/{provider}/callback", auth.SignInOAuthCallback)
|
||||
|
|
|
@ -8,41 +8,38 @@ export function initCompSearchUserBox() {
|
|||
const searchUserBox = document.getElementById('search-user-box');
|
||||
if (!searchUserBox) return;
|
||||
|
||||
const $searchUserBox = $(searchUserBox);
|
||||
const allowEmailInput = searchUserBox.getAttribute('data-allow-email') === 'true';
|
||||
const allowEmailDescription = searchUserBox.getAttribute('data-allow-email-description') ?? undefined;
|
||||
$searchUserBox.search({
|
||||
$(searchUserBox).search({
|
||||
minCharacters: 2,
|
||||
apiSettings: {
|
||||
url: `${appSubUrl}/user/search?active=1&q={query}`,
|
||||
url: `${appSubUrl}/user/search_candidates?q={query}`,
|
||||
onResponse(response) {
|
||||
const items = [];
|
||||
const searchQuery = $searchUserBox.find('input').val();
|
||||
const resultItems = [];
|
||||
const searchQuery = searchUserBox.querySelector('input').value;
|
||||
const searchQueryUppercase = searchQuery.toUpperCase();
|
||||
$.each(response.data, (_i, item) => {
|
||||
for (const item of response.data) {
|
||||
const resultItem = {
|
||||
title: item.login,
|
||||
image: item.avatar_url,
|
||||
description: htmlEscape(item.full_name),
|
||||
};
|
||||
if (item.full_name) {
|
||||
resultItem.description = htmlEscape(item.full_name);
|
||||
}
|
||||
if (searchQueryUppercase === item.login.toUpperCase()) {
|
||||
items.unshift(resultItem);
|
||||
resultItems.unshift(resultItem); // add the exact match to the top
|
||||
} else {
|
||||
items.push(resultItem);
|
||||
resultItems.push(resultItem);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (allowEmailInput && !items.length && looksLikeEmailAddressCheck.test(searchQuery)) {
|
||||
if (allowEmailInput && !resultItems.length && looksLikeEmailAddressCheck.test(searchQuery)) {
|
||||
const resultItem = {
|
||||
title: searchQuery,
|
||||
description: allowEmailDescription,
|
||||
};
|
||||
items.push(resultItem);
|
||||
resultItems.push(resultItem);
|
||||
}
|
||||
|
||||
return {results: items};
|
||||
return {results: resultItems};
|
||||
},
|
||||
},
|
||||
searchFields: ['login', 'full_name'],
|
||||
|
|
Loading…
Reference in New Issue