diff --git a/models/organization/org_list.go b/models/organization/org_list.go index 4c4168af1f..78ac0e704a 100644 --- a/models/organization/org_list.go +++ b/models/organization/org_list.go @@ -124,6 +124,7 @@ func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, if err := db.GetEngine(ctx).Select(columnsStr). Table("user"). Where(builder.In("`user`.`id`", queryUserOrgIDs(user.ID, true))). + OrderBy("`user`.lower_name ASC"). Find(&orgs); err != nil { return nil, err } diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go index a9b1360df1..232087d865 100644 --- a/models/repo/user_repo.go +++ b/models/repo/user_repo.go @@ -5,6 +5,7 @@ package repo import ( "context" + "strings" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" @@ -149,9 +150,9 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us // If isShowFullName is set to true, also include full name prefix search func GetIssuePostersWithSearch(ctx context.Context, repo *Repository, isPull bool, search string, isShowFullName bool) ([]*user_model.User, error) { users := make([]*user_model.User, 0, 30) - var prefixCond builder.Cond = builder.Like{"name", search + "%"} + var prefixCond builder.Cond = builder.Like{"lower_name", strings.ToLower(search) + "%"} if isShowFullName { - prefixCond = prefixCond.Or(builder.Like{"full_name", "%" + search + "%"}) + prefixCond = prefixCond.Or(db.BuildCaseInsensitiveLike("full_name", "%"+search+"%")) } cond := builder.In("`user`.id", diff --git a/models/repo/user_repo_test.go b/models/repo/user_repo_test.go index 44ebe5f214..50c970344c 100644 --- a/models/repo/user_repo_test.go +++ b/models/repo/user_repo_test.go @@ -12,6 +12,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestRepoAssignees(t *testing.T) { @@ -38,3 +39,19 @@ func TestRepoAssignees(t *testing.T) { assert.NotContains(t, []int64{users[0].ID, users[1].ID, users[2].ID}, 15) } } + +func TestGetIssuePostersWithSearch(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + + users, err := repo_model.GetIssuePostersWithSearch(db.DefaultContext, repo2, false, "USER", false /* full name */) + require.NoError(t, err) + require.Len(t, users, 1) + assert.Equal(t, "user2", users[0].Name) + + users, err = repo_model.GetIssuePostersWithSearch(db.DefaultContext, repo2, false, "TW%O", true /* full name */) + require.NoError(t, err) + require.Len(t, users, 1) + assert.Equal(t, "user2", users[0].Name) +} diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index c397d7972b..a54225f0fd 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -7,6 +7,7 @@ package repo import ( "errors" "net/http" + "strings" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" @@ -274,12 +275,13 @@ func GetRepoPermissions(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - if !ctx.Doer.IsAdmin && ctx.Doer.LoginName != ctx.PathParam("collaborator") && !ctx.IsUserRepoAdmin() { + collaboratorUsername := ctx.PathParam("collaborator") + if !ctx.Doer.IsAdmin && ctx.Doer.LowerName != strings.ToLower(collaboratorUsername) && !ctx.IsUserRepoAdmin() { ctx.APIError(http.StatusForbidden, "Only admins can query all permissions, repo admins can query all repo permissions, collaborators can query only their own") return } - collaborator, err := user_model.GetUserByName(ctx, ctx.PathParam("collaborator")) + collaborator, err := user_model.GetUserByName(ctx, collaboratorUsername) if err != nil { if user_model.IsErrUserNotExist(err) { ctx.APIError(http.StatusNotFound, err) diff --git a/tests/integration/api_repo_collaborator_test.go b/tests/integration/api_repo_collaborator_test.go index 463db1dfb1..11e2924e84 100644 --- a/tests/integration/api_repo_collaborator_test.go +++ b/tests/integration/api_repo_collaborator_test.go @@ -5,7 +5,6 @@ package integration import ( "net/http" - "net/url" "testing" auth_model "code.gitea.io/gitea/models/auth" @@ -14,132 +13,145 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" ) func TestAPIRepoCollaboratorPermission(t *testing.T) { - onGiteaRun(t, func(t *testing.T, u *url.URL) { - repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) - repo2Owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo2.OwnerID}) + defer tests.PrepareTestEnv(t)() + repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + repo2Owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo2.OwnerID}) - user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) - user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) - user10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10}) - user11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 11}) - user34 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 34}) + user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) + user10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10}) + user11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 11}) + user34 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 34}) - testCtx := NewAPITestContext(t, repo2Owner.Name, repo2.Name, auth_model.AccessTokenScopeWriteRepository) + testCtx := NewAPITestContext(t, repo2Owner.Name, repo2.Name, auth_model.AccessTokenScopeWriteRepository) - t.Run("RepoOwnerShouldBeOwner", func(t *testing.T) { - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, repo2Owner.Name). - AddTokenAuth(testCtx.Token) - resp := MakeRequest(t, req, http.StatusOK) + t.Run("RepoOwnerShouldBeOwner", func(t *testing.T) { + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, repo2Owner.Name). + AddTokenAuth(testCtx.Token) + resp := MakeRequest(t, req, http.StatusOK) - var repoPermission api.RepoCollaboratorPermission - DecodeJSON(t, resp, &repoPermission) + var repoPermission api.RepoCollaboratorPermission + DecodeJSON(t, resp, &repoPermission) - assert.Equal(t, "owner", repoPermission.Permission) - }) + assert.Equal(t, "owner", repoPermission.Permission) + }) - t.Run("CollaboratorWithReadAccess", func(t *testing.T) { - t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user4.Name, perm.AccessModeRead)) + t.Run("CollaboratorWithReadAccess", func(t *testing.T) { + t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user4.Name, perm.AccessModeRead)) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user4.Name). - AddTokenAuth(testCtx.Token) - resp := MakeRequest(t, req, http.StatusOK) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user4.Name). + AddTokenAuth(testCtx.Token) + resp := MakeRequest(t, req, http.StatusOK) - var repoPermission api.RepoCollaboratorPermission - DecodeJSON(t, resp, &repoPermission) + var repoPermission api.RepoCollaboratorPermission + DecodeJSON(t, resp, &repoPermission) - assert.Equal(t, "read", repoPermission.Permission) - }) + assert.Equal(t, "read", repoPermission.Permission) + }) - t.Run("CollaboratorWithWriteAccess", func(t *testing.T) { - t.Run("AddUserAsCollaboratorWithWriteAccess", doAPIAddCollaborator(testCtx, user4.Name, perm.AccessModeWrite)) + t.Run("CollaboratorWithWriteAccess", func(t *testing.T) { + t.Run("AddUserAsCollaboratorWithWriteAccess", doAPIAddCollaborator(testCtx, user4.Name, perm.AccessModeWrite)) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user4.Name). - AddTokenAuth(testCtx.Token) - resp := MakeRequest(t, req, http.StatusOK) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user4.Name). + AddTokenAuth(testCtx.Token) + resp := MakeRequest(t, req, http.StatusOK) - var repoPermission api.RepoCollaboratorPermission - DecodeJSON(t, resp, &repoPermission) + var repoPermission api.RepoCollaboratorPermission + DecodeJSON(t, resp, &repoPermission) - assert.Equal(t, "write", repoPermission.Permission) - }) + assert.Equal(t, "write", repoPermission.Permission) + }) - t.Run("CollaboratorWithAdminAccess", func(t *testing.T) { - t.Run("AddUserAsCollaboratorWithAdminAccess", doAPIAddCollaborator(testCtx, user4.Name, perm.AccessModeAdmin)) + t.Run("CollaboratorWithAdminAccess", func(t *testing.T) { + t.Run("AddUserAsCollaboratorWithAdminAccess", doAPIAddCollaborator(testCtx, user4.Name, perm.AccessModeAdmin)) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user4.Name). - AddTokenAuth(testCtx.Token) - resp := MakeRequest(t, req, http.StatusOK) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user4.Name). + AddTokenAuth(testCtx.Token) + resp := MakeRequest(t, req, http.StatusOK) - var repoPermission api.RepoCollaboratorPermission - DecodeJSON(t, resp, &repoPermission) + var repoPermission api.RepoCollaboratorPermission + DecodeJSON(t, resp, &repoPermission) - assert.Equal(t, "admin", repoPermission.Permission) - }) + assert.Equal(t, "admin", repoPermission.Permission) + }) - t.Run("CollaboratorNotFound", func(t *testing.T) { - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, "non-existent-user"). - AddTokenAuth(testCtx.Token) - MakeRequest(t, req, http.StatusNotFound) - }) + t.Run("CollaboratorNotFound", func(t *testing.T) { + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, "non-existent-user"). + AddTokenAuth(testCtx.Token) + MakeRequest(t, req, http.StatusNotFound) + }) - t.Run("CollaboratorBlocked", func(t *testing.T) { - ctx := NewAPITestContext(t, repo2Owner.Name, repo2.Name, auth_model.AccessTokenScopeWriteRepository) - ctx.ExpectedCode = http.StatusForbidden - doAPIAddCollaborator(ctx, user34.Name, perm.AccessModeAdmin)(t) - }) + t.Run("CollaboratorBlocked", func(t *testing.T) { + ctx := NewAPITestContext(t, repo2Owner.Name, repo2.Name, auth_model.AccessTokenScopeWriteRepository) + ctx.ExpectedCode = http.StatusForbidden + doAPIAddCollaborator(ctx, user34.Name, perm.AccessModeAdmin)(t) + }) - t.Run("CollaboratorCanQueryItsPermissions", func(t *testing.T) { - t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) + t.Run("CollaboratorCanQueryItsPermissions", func(t *testing.T) { + t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) - _session := loginUser(t, user5.Name) - _testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository) + _session := loginUser(t, user5.Name) + _testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user5.Name). - AddTokenAuth(_testCtx.Token) - resp := _session.MakeRequest(t, req, http.StatusOK) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user5.Name). + AddTokenAuth(_testCtx.Token) + resp := _session.MakeRequest(t, req, http.StatusOK) - var repoPermission api.RepoCollaboratorPermission - DecodeJSON(t, resp, &repoPermission) + var repoPermission api.RepoCollaboratorPermission + DecodeJSON(t, resp, &repoPermission) - assert.Equal(t, "read", repoPermission.Permission) - }) + assert.Equal(t, "read", repoPermission.Permission) - t.Run("CollaboratorCanQueryItsPermissions", func(t *testing.T) { - t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) + t.Run("CollaboratorCanReadOwnPermission", func(t *testing.T) { + session := loginUser(t, user5.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) - _session := loginUser(t, user5.Name) - _testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository) + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user5.Name).AddTokenAuth(token) + resp = MakeRequest(t, req, http.StatusOK) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user5.Name). - AddTokenAuth(_testCtx.Token) - resp := _session.MakeRequest(t, req, http.StatusOK) + repoCollPerm := api.RepoCollaboratorPermission{} + DecodeJSON(t, resp, &repoCollPerm) - var repoPermission api.RepoCollaboratorPermission - DecodeJSON(t, resp, &repoPermission) - - assert.Equal(t, "read", repoPermission.Permission) - }) - - t.Run("RepoAdminCanQueryACollaboratorsPermissions", func(t *testing.T) { - t.Run("AddUserAsCollaboratorWithAdminAccess", doAPIAddCollaborator(testCtx, user10.Name, perm.AccessModeAdmin)) - t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user11.Name, perm.AccessModeRead)) - - _session := loginUser(t, user10.Name) - _testCtx := NewAPITestContext(t, user10.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository) - - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user11.Name). - AddTokenAuth(_testCtx.Token) - resp := _session.MakeRequest(t, req, http.StatusOK) - - var repoPermission api.RepoCollaboratorPermission - DecodeJSON(t, resp, &repoPermission) - - assert.Equal(t, "read", repoPermission.Permission) + assert.Equal(t, "read", repoCollPerm.Permission) }) }) + + t.Run("CollaboratorCanQueryItsPermissions", func(t *testing.T) { + t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) + + _session := loginUser(t, user5.Name) + _testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository) + + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user5.Name). + AddTokenAuth(_testCtx.Token) + resp := _session.MakeRequest(t, req, http.StatusOK) + + var repoPermission api.RepoCollaboratorPermission + DecodeJSON(t, resp, &repoPermission) + + assert.Equal(t, "read", repoPermission.Permission) + }) + + t.Run("RepoAdminCanQueryACollaboratorsPermissions", func(t *testing.T) { + t.Run("AddUserAsCollaboratorWithAdminAccess", doAPIAddCollaborator(testCtx, user10.Name, perm.AccessModeAdmin)) + t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user11.Name, perm.AccessModeRead)) + + _session := loginUser(t, user10.Name) + _testCtx := NewAPITestContext(t, user10.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository) + + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user11.Name). + AddTokenAuth(_testCtx.Token) + resp := _session.MakeRequest(t, req, http.StatusOK) + + var repoPermission api.RepoCollaboratorPermission + DecodeJSON(t, resp, &repoPermission) + + assert.Equal(t, "read", repoPermission.Permission) + }) }