feat: update gitea vendor
runner nix smoke / nix label and flake smoke (push) Failing after 1m28s

This commit is contained in:
2026-09-26 21:18:24 +00:00
parent c439c1b948
commit d9b2a4e787
3538 changed files with 116131 additions and 44340 deletions
@@ -6,14 +6,14 @@ package organization_test
import (
"testing"
"code.gitea.io/gitea/models/unittest"
"gitea.dev/models/unittest"
_ "code.gitea.io/gitea/models"
_ "code.gitea.io/gitea/models/actions"
_ "code.gitea.io/gitea/models/activities"
_ "code.gitea.io/gitea/models/organization"
_ "code.gitea.io/gitea/models/repo"
_ "code.gitea.io/gitea/models/user"
_ "gitea.dev/models"
_ "gitea.dev/models/actions"
_ "gitea.dev/models/activities"
_ "gitea.dev/models/organization"
_ "gitea.dev/models/repo"
_ "gitea.dev/models/user"
)
func TestMain(m *testing.M) {
+65 -23
View File
@@ -9,17 +9,16 @@ import (
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"gitea.dev/models/db"
"gitea.dev/models/perm"
"gitea.dev/models/unit"
user_model "gitea.dev/models/user"
"gitea.dev/modules/log"
"gitea.dev/modules/setting"
"gitea.dev/modules/structs"
"gitea.dev/modules/util"
"xorm.io/builder"
"xorm.io/xorm"
)
// ErrOrgNotExist represents a "OrgNotExist" kind of error.
@@ -92,6 +91,14 @@ func (org *Organization) IsOwnedBy(ctx context.Context, uid int64) (bool, error)
return IsOrganizationOwner(ctx, org.ID, uid)
}
// CanChangeRepoTeamAccess reports whether a repository administrator can change team access.
func (org *Organization) CanChangeRepoTeamAccess(ctx context.Context, doer *user_model.User) (bool, error) {
if org.RepoAdminChangeTeamAccess || doer.IsAdmin {
return true, nil
}
return org.IsOwnedBy(ctx, doer.ID)
}
// IsOrgAdmin returns true if given user is in the owner team or an admin team.
func (org *Organization) IsOrgAdmin(ctx context.Context, uid int64) (bool, error) {
return IsOrganizationAdmin(ctx, org.ID, uid)
@@ -184,14 +191,44 @@ type FindOrgMembersOpts struct {
Doer *user_model.User
IsDoerMember bool
OrgID int64
Keyword string
}
func (opts FindOrgMembersOpts) PublicOnly() bool {
return opts.Doer == nil || !(opts.IsDoerMember || opts.Doer.IsAdmin)
}
// applyKeywordFilter adds keyword search conditions to session
func (opts FindOrgMembersOpts) applyKeywordFilter(sess db.Session) bool {
if opts.Keyword == "" {
return false
}
keywordCond := builder.Or(
db.BuildCaseInsensitiveLike("`user`.lower_name", opts.Keyword),
db.BuildCaseInsensitiveLike("`user`.full_name", opts.Keyword),
)
emailCond := db.BuildCaseInsensitiveLike("`user`.email", opts.Keyword)
switch {
case opts.Doer == nil:
emailCond = emailCond.And(builder.Eq{"`user`.keep_email_private": false})
case !opts.Doer.IsAdmin:
emailCond = emailCond.And(
builder.Or(
builder.Eq{"`user`.keep_email_private": false},
builder.Eq{"`user`.id": opts.Doer.ID},
),
)
}
keywordCond = keywordCond.Or(emailCond)
_ = sess.Join("INNER", "`user`", "org_user.uid = `user`.id").And(keywordCond)
return true
}
// applyTeamMatesOnlyFilter make sure restricted users only see public team members and there own team mates
func (opts FindOrgMembersOpts) applyTeamMatesOnlyFilter(sess *xorm.Session) {
func (opts FindOrgMembersOpts) applyTeamMatesOnlyFilter(sess db.Session) {
if opts.Doer != nil && opts.IsDoerMember && opts.Doer.IsRestricted {
teamMates := builder.Select("DISTINCT team_user.uid").
From("team_user").
@@ -213,6 +250,7 @@ func CountOrgMembers(ctx context.Context, opts *FindOrgMembersOpts) (int64, erro
} else {
opts.applyTeamMatesOnlyFilter(sess)
}
_ = opts.applyKeywordFilter(sess)
return sess.Count(new(OrgUser))
}
@@ -273,7 +311,7 @@ func (org *Organization) UnitPermission(ctx context.Context, doer *user_model.Us
}
}
if org.Visibility.IsPublic() {
if ownerVisibilitySatisfiesDoer(org.AsUser(), doer) {
return perm.AccessModeRead
}
@@ -340,6 +378,7 @@ func CreateOrganization(ctx context.Context, org *Organization, owner *user_mode
NumMembers: 1,
IncludesAllRepositories: true,
CanCreateOrgRepo: true,
Visibility: structs.VisibleTypeLimited,
}
if err = db.Insert(ctx, t); err != nil {
return fmt.Errorf("insert owner team: %w", err)
@@ -380,11 +419,8 @@ func GetOrgByName(ctx context.Context, name string) (*Organization, error) {
if len(name) == 0 {
return nil, ErrOrgNotExist{0, name}
}
u := &Organization{
LowerName: strings.ToLower(name),
Type: user_model.UserTypeOrganization,
}
has, err := db.GetEngine(ctx).Get(u)
u, has, err := db.Get[Organization](ctx, builder.Eq{"lower_name": strings.ToLower(name), "`type`": user_model.UserTypeOrganization})
if err != nil {
return nil, err
} else if !has {
@@ -417,8 +453,7 @@ func GetUsersWhoCanCreateOrgRepo(ctx context.Context, orgID int64) (map[int64]*u
And("team_user.org_id = ?", orgID).Find(&users)
}
// HasOrgOrUserVisible tells if the given user can see the given org or user
func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool {
func ownerVisibilitySatisfiesDoer(orgOrUser, user *user_model.User) bool {
// If user is nil, it's an anonymous user/request.
// The Ghost user is handled like an anonymous user.
if user == nil || user.IsGhost() {
@@ -433,10 +468,13 @@ func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User)
return true
}
if (orgOrUser.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !OrgFromUser(orgOrUser).hasMemberWithUserID(ctx, user.ID) {
return false
}
return true
return orgOrUser.Visibility != structs.VisibleTypePrivate && !user.IsRestricted
}
// HasOrgOrUserVisible tells if the given user can see the given org or user
func HasOrgOrUserVisible(ctx context.Context, owner, doer *user_model.User) bool {
return ownerVisibilitySatisfiesDoer(owner, doer) ||
(doer != nil && OrgFromUser(owner).HasMemberWithUserID(ctx, doer.ID))
}
// HasOrgsVisible tells if the given user can see at least one of the orgs provided
@@ -461,9 +499,13 @@ func GetOrgUsersByOrgID(ctx context.Context, opts *FindOrgMembersOpts) ([]*OrgUs
} else {
opts.applyTeamMatesOnlyFilter(sess)
}
if opts.applyKeywordFilter(sess) {
sess = sess.Select("org_user.*")
}
sess = sess.OrderBy("org_user.uid ASC")
if opts.ListOptions.PageSize > 0 {
sess = db.SetSessionPagination(sess, opts)
db.SetSessionPagination(sess, opts)
ous := make([]*OrgUser, 0, opts.PageSize)
return ous, sess.Find(&ous)
@@ -8,10 +8,10 @@ import (
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/structs"
"gitea.dev/models/db"
"gitea.dev/models/perm"
user_model "gitea.dev/models/user"
"gitea.dev/modules/structs"
"xorm.io/builder"
)
@@ -89,6 +89,9 @@ func DoerViewOtherVisibility(doer, other *user_model.User) structs.VisibleType {
if doer.IsAdmin || doer.ID == other.ID {
return structs.VisibleTypePrivate
}
if doer.IsRestricted {
return structs.VisibleTypePublic
}
return structs.VisibleTypeLimited
}
@@ -6,11 +6,11 @@ package organization_test
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/structs"
"gitea.dev/models/db"
"gitea.dev/models/organization"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"gitea.dev/modules/structs"
"github.com/stretchr/testify/assert"
)
@@ -77,8 +77,14 @@ func testLoadOrgListTeams(t *testing.T) {
}
func testDoerViewOtherVisibility(t *testing.T) {
viewer := &user_model.User{ID: 1}
other := &user_model.User{ID: 2}
restrictedViewer := &user_model.User{ID: 3, IsRestricted: true}
assert.Equal(t, structs.VisibleTypePublic, organization.DoerViewOtherVisibility(nil, nil))
assert.Equal(t, structs.VisibleTypeLimited, organization.DoerViewOtherVisibility(&user_model.User{ID: 1}, &user_model.User{ID: 2}))
assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(&user_model.User{ID: 1}, &user_model.User{ID: 1}))
assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(&user_model.User{ID: 1, IsAdmin: true}, &user_model.User{ID: 2}))
assert.Equal(t, structs.VisibleTypeLimited, organization.DoerViewOtherVisibility(viewer, other))
assert.Equal(t, structs.VisibleTypePublic, organization.DoerViewOtherVisibility(restrictedViewer, other))
assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(viewer, viewer))
assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(restrictedViewer, restrictedViewer))
assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(&user_model.User{ID: 4, IsAdmin: true, IsRestricted: true}, other))
}
@@ -8,14 +8,16 @@ import (
"sort"
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
"gitea.dev/models/db"
"gitea.dev/models/organization"
"gitea.dev/models/perm"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unit"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"gitea.dev/modules/setting"
"gitea.dev/modules/structs"
"gitea.dev/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -288,6 +290,76 @@ func TestGetOrgUsersByOrgID(t *testing.T) {
assert.Empty(t, orgUsers)
}
func TestOrgMembersSearch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
member := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
testCases := []struct {
name string
opts *organization.FindOrgMembersOpts
expectedUIDs []int64
}{
{
name: "match by username",
opts: &organization.FindOrgMembersOpts{
OrgID: 3,
Doer: member,
IsDoerMember: true,
Keyword: "user4",
},
expectedUIDs: []int64{4},
},
{
name: "match by full name",
opts: &organization.FindOrgMembersOpts{
OrgID: 3,
Doer: member,
IsDoerMember: true,
Keyword: "user27",
},
expectedUIDs: []int64{28},
},
{
name: "private email hidden",
opts: &organization.FindOrgMembersOpts{
OrgID: 3,
Doer: member,
IsDoerMember: true,
Keyword: "user2@example.com",
},
expectedUIDs: []int64{},
},
{
name: "admin can search private email",
opts: &organization.FindOrgMembersOpts{
OrgID: 3,
Doer: admin,
Keyword: "user2@example.com",
},
expectedUIDs: []int64{2},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
count, err := organization.CountOrgMembers(t.Context(), tc.opts)
assert.NoError(t, err)
assert.EqualValues(t, len(tc.expectedUIDs), count)
members, err := organization.GetOrgUsersByOrgID(t.Context(), tc.opts)
assert.NoError(t, err)
memberUIDs := make([]int64, 0, len(members))
for _, member := range members {
memberUIDs = append(memberUIDs, member.UID)
}
slices.Sort(memberUIDs)
assert.Equal(t, tc.expectedUIDs, memberUIDs)
})
}
}
func TestChangeOrgUserStatus(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
@@ -456,6 +528,22 @@ func TestGetUsersWhoCanCreateOrgRepo(t *testing.T) {
assert.NotNil(t, users[5])
}
func TestCanCreateOrgRepoByOwnerTeamWithoutFlag(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
ownerTeam, err := org.GetOwnerTeam(t.Context())
require.NoError(t, err)
ownerTeam.CanCreateOrgRepo = false
_, err = db.GetEngine(t.Context()).ID(ownerTeam.ID).Cols("can_create_org_repo").Update(ownerTeam)
require.NoError(t, err)
ok, err := organization.CanCreateOrgRepo(t.Context(), org.ID, 2)
require.NoError(t, err)
assert.True(t, ok)
}
func TestUser_RemoveOrgRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
@@ -540,3 +628,10 @@ func TestCreateOrganization4(t *testing.T) {
assert.True(t, db.IsErrNameReserved(err))
unittest.CheckConsistencyFor(t, &organization.Organization{}, &organization.Team{})
}
func TestOrAnyRepoUnitPermission(t *testing.T) {
defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)()
org := organization.Organization{Visibility: structs.VisibleTypeLimited}
assert.Equal(t, perm.AccessModeNone, org.UnitPermission(t.Context(), nil, unit.TypeWiki))
assert.Equal(t, perm.AccessModeRead, org.UnitPermission(t.Context(), &user_model.User{}, unit.TypeWiki))
}
@@ -7,9 +7,10 @@ import (
"context"
"fmt"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"gitea.dev/models/db"
"gitea.dev/models/perm"
user_model "gitea.dev/models/user"
"gitea.dev/modules/log"
"xorm.io/builder"
)
@@ -104,7 +105,7 @@ func IsPublicMembership(ctx context.Context, orgID, uid int64) (bool, error) {
// CanCreateOrgRepo returns true if user can create repo in organization
func CanCreateOrgRepo(ctx context.Context, orgID, uid int64) (bool, error) {
return db.GetEngine(ctx).
Where(builder.Eq{"team.can_create_org_repo": true}).
Where(builder.Eq{"team.can_create_org_repo": true}.Or(builder.Eq{"team.authorize": perm.AccessModeOwner})).
Join("INNER", "team_user", "team_user.team_id = team.id").
And("team_user.uid = ?", uid).
And("team_user.org_id = ?", orgID).
@@ -7,10 +7,10 @@ import (
"fmt"
"testing"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"gitea.dev/models/organization"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"gitea.dev/modules/setting"
"github.com/stretchr/testify/assert"
)
@@ -7,7 +7,7 @@ import (
"context"
"sort"
"code.gitea.io/gitea/models/db"
"gitea.dev/models/db"
"xorm.io/builder"
)
@@ -6,15 +6,17 @@ package organization
import (
"context"
"errors"
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"gitea.dev/models/db"
"gitea.dev/models/perm"
"gitea.dev/models/unit"
user_model "gitea.dev/models/user"
"gitea.dev/modules/log"
"gitea.dev/modules/structs"
"gitea.dev/modules/util"
"xorm.io/builder"
)
@@ -81,9 +83,45 @@ type Team struct {
Members []*user_model.User `xorm:"-"`
NumRepos int
NumMembers int
Units []*TeamUnit `xorm:"-"`
IncludesAllRepositories bool `xorm:"NOT NULL DEFAULT false"`
CanCreateOrgRepo bool `xorm:"NOT NULL DEFAULT false"`
Units []*TeamUnit `xorm:"-"`
IncludesAllRepositories bool `xorm:"NOT NULL DEFAULT false"`
CanCreateOrgRepo bool `xorm:"NOT NULL DEFAULT false"`
Visibility structs.VisibleType `xorm:"NOT NULL DEFAULT 2"`
}
func (t *Team) IsPublic() bool { return t.Visibility.IsPublic() }
func (t *Team) IsLimited() bool { return t.Visibility.IsLimited() }
func (t *Team) IsPrivate() bool { return t.Visibility.IsPrivate() }
const (
ghostTeamID = -1
ghostTeamName = "(deleted team)"
)
func newGhostTeam() *Team {
return &Team{ID: ghostTeamID, Name: ghostTeamName, LowerName: ghostTeamName}
}
// CanNonMemberReadMeta reports whether a non-member, non-owner doer may read
// the team's metadata, based on the team's visibility tier and the parent org's
// visibility. Privileged callers (site admins, org owners, team members) are
// decided by the caller before reaching here.
func (t *Team) CanNonMemberReadMeta(ctx context.Context, org, doer *user_model.User) (bool, error) {
switch t.Visibility {
case structs.VisibleTypePublic:
return HasOrgOrUserVisible(ctx, org, doer), nil
case structs.VisibleTypeLimited:
return IsOrganizationMember(ctx, t.OrgID, doer.ID)
default:
return false, nil
}
}
func NormalizeTeamVisibility(s string) structs.VisibleType {
if vt, ok := structs.VisibilityModes[s]; ok {
return vt
}
return structs.VisibleTypePrivate
}
func init() {
@@ -242,6 +280,17 @@ func GetTeamByID(ctx context.Context, teamID int64) (*Team, error) {
return t, nil
}
func GetPossibleTeamByID(ctx context.Context, teamID int64) (int64, *Team, error) {
t, err := GetTeamByID(ctx, teamID)
if errors.Is(err, util.ErrNotExist) {
t = newGhostTeam()
return t.ID, t, nil
} else if err != nil {
return 0, nil, err
}
return t.ID, t, nil
}
// IncrTeamRepoNum increases the number of repos for the given team by 1
func IncrTeamRepoNum(ctx context.Context, teamID int64) error {
_, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team))
@@ -7,10 +7,10 @@ import (
"context"
"fmt"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"gitea.dev/models/db"
user_model "gitea.dev/models/user"
"gitea.dev/modules/timeutil"
"gitea.dev/modules/util"
"xorm.io/builder"
)
@@ -116,10 +116,7 @@ func CreateTeamInvite(ctx context.Context, doer *user_model.User, team *Team, em
}
}
token, err := util.CryptoRandomString(25)
if err != nil {
return nil, err
}
token := util.CryptoRandomString(25)
invite := &TeamInvite{
Token: token,
@@ -6,9 +6,9 @@ package organization_test
import (
"testing"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"gitea.dev/models/organization"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"github.com/stretchr/testify/assert"
)
@@ -7,9 +7,11 @@ import (
"context"
"strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
"gitea.dev/models/db"
"gitea.dev/models/perm"
"gitea.dev/models/unit"
user_model "gitea.dev/models/user"
"gitea.dev/modules/structs"
"xorm.io/builder"
)
@@ -50,9 +52,15 @@ type SearchTeamOptions struct {
Keyword string
OrgID int64
IncludeDesc bool
// IncludeVisibilities, when combined with UserID, also returns teams whose
// visibility is in this list, even if UserID is not a member. Typical values:
// - {limited,public} for org members
// - {public} for signed-in users who are not org members
// Leave empty to return only teams the user is a member of.
IncludeVisibilities []structs.VisibleType
}
func (opts *SearchTeamOptions) toCond() builder.Cond {
func (opts *SearchTeamOptions) applyToSession(sess db.SQLSession) {
cond := builder.NewCond()
if len(opts.Keyword) > 0 {
@@ -68,11 +76,51 @@ func (opts *SearchTeamOptions) toCond() builder.Cond {
cond = cond.And(builder.Eq{"`team`.org_id": opts.OrgID})
}
if opts.UserID > 0 {
switch {
case opts.UserID > 0 && len(opts.IncludeVisibilities) > 0:
sess = sess.Join("LEFT", "team_user", "team_user.team_id = team.id AND team_user.uid = ?", opts.UserID)
cond = cond.And(builder.Or(
builder.Eq{"team_user.uid": opts.UserID},
builder.In("`team`.visibility", opts.IncludeVisibilities),
))
case opts.UserID > 0:
sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id")
cond = cond.And(builder.Eq{"team_user.uid": opts.UserID})
case len(opts.IncludeVisibilities) > 0:
cond = cond.And(builder.In("`team`.visibility", opts.IncludeVisibilities))
}
sess.Where(cond)
}
return cond
func VisibleTeamVisibilitiesFor(isOrgMember, isSignedIn bool) []structs.VisibleType {
switch {
case isOrgMember:
return []structs.VisibleType{structs.VisibleTypeLimited, structs.VisibleTypePublic}
case isSignedIn:
return []structs.VisibleType{structs.VisibleTypePublic}
default:
return nil
}
}
func ApplyTeamListFilter(ctx context.Context, orgID int64, viewer *user_model.User, isSignedIn bool, opts *SearchTeamOptions) error {
if viewer.IsAdmin {
return nil
}
isOwner, err := IsOrganizationOwner(ctx, orgID, viewer.ID)
if err != nil {
return err
}
if isOwner {
return nil
}
isOrgMember, err := IsOrganizationMember(ctx, orgID, viewer.ID)
if err != nil {
return err
}
opts.UserID = viewer.ID
opts.IncludeVisibilities = VisibleTeamVisibilitiesFor(isOrgMember, isSignedIn)
return nil
}
// SearchTeam search for teams. Caller is responsible to check permissions.
@@ -80,15 +128,12 @@ func SearchTeam(ctx context.Context, opts *SearchTeamOptions) (TeamList, int64,
sess := db.GetEngine(ctx)
opts.SetDefaultValues()
cond := opts.toCond()
opts.applyToSession(sess)
if opts.UserID > 0 {
sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id")
}
sess = db.SetSessionPagination(sess, opts)
db.SetSessionPagination(sess, opts)
teams := make([]*Team, 0, opts.PageSize)
count, err := sess.Where(cond).OrderBy("lower_name").FindAndCount(&teams)
count, err := sess.OrderBy("CASE WHEN name=? THEN '' ELSE lower_name END", OwnerTeamName).FindAndCount(&teams)
if err != nil {
return nil, 0, err
}
@@ -6,8 +6,8 @@ package organization_test
import (
"testing"
org_model "code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/unittest"
org_model "gitea.dev/models/organization"
"gitea.dev/models/unittest"
"github.com/stretchr/testify/assert"
)
@@ -6,9 +6,9 @@ package organization
import (
"context"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
"gitea.dev/models/db"
"gitea.dev/models/perm"
"gitea.dev/models/unit"
"xorm.io/builder"
)
@@ -6,11 +6,11 @@ package organization_test
import (
"testing"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
"gitea.dev/models/organization"
"gitea.dev/models/perm"
"gitea.dev/models/repo"
"gitea.dev/models/unit"
"gitea.dev/models/unittest"
"github.com/stretchr/testify/assert"
)
@@ -6,10 +6,12 @@ package organization_test
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"gitea.dev/models/db"
"gitea.dev/models/organization"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"gitea.dev/modules/structs"
"github.com/stretchr/testify/assert"
)
@@ -38,6 +40,43 @@ func TestTeam_IsMember(t *testing.T) {
assert.False(t, team.IsMember(t.Context(), unittest.NonexistentID))
}
func TestTeam_CanNonMemberReadMeta(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) // public org
org35 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 35}) // private org
member := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // member of org 3 and org 35
outsider := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) // member of neither org
test := func(name string, team *organization.Team, org, doer *user_model.User, expected bool) {
t.Run(name, func(t *testing.T) {
ok, err := team.CanNonMemberReadMeta(t.Context(), org, doer)
assert.NoError(t, err)
assert.Equal(t, expected, ok)
})
}
// Public team is gated only by the parent org's visibility.
publicTeam := &organization.Team{OrgID: 3, Visibility: structs.VisibleTypePublic}
test("public team, public org, member", publicTeam, org3, member, true)
test("public team, public org, outsider", publicTeam, org3, outsider, true)
// Public team inside a private org: only org members may see it.
publicTeamPrivOrg := &organization.Team{OrgID: 35, Visibility: structs.VisibleTypePublic}
test("public team, private org, org member", publicTeamPrivOrg, org35, member, true)
test("public team, private org, outsider", publicTeamPrivOrg, org35, outsider, false)
// Limited team: any org member, but never outsiders.
limitedTeam := &organization.Team{OrgID: 3, Visibility: structs.VisibleTypeLimited}
test("limited team, org member", limitedTeam, org3, member, true)
test("limited team, outsider", limitedTeam, org3, outsider, false)
// Private team is never visible to non-members; members/owners are admitted by the caller.
privateTeam := &organization.Team{OrgID: 3, Visibility: structs.VisibleTypePrivate}
test("private team, org member", privateTeam, org3, member, false)
test("private team, outsider", privateTeam, org3, outsider, false)
}
func TestTeam_GetRepositories(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
@@ -172,6 +211,52 @@ func TestGetUserOrgTeams(t *testing.T) {
test(3, unittest.NonexistentID)
}
func TestSearchTeamIncludeVisible(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
const orgID int64 = 3
// User 5 is an org member but only belongs to team 1 (Owners) — make sure
// they don't see team 2 (default private) but do see a freshly added
// limited team they are not a member of.
visible := &organization.Team{
OrgID: orgID,
LowerName: "visible-team",
Name: "visible-team",
AccessMode: 1, // read
Visibility: structs.VisibleTypeLimited,
}
assert.NoError(t, db.Insert(t.Context(), visible))
teams, _, err := organization.SearchTeam(t.Context(), &organization.SearchTeamOptions{
OrgID: orgID,
UserID: 2,
IncludeVisibilities: organization.VisibleTeamVisibilitiesFor(true, true),
})
assert.NoError(t, err)
ids := make(map[int64]bool, len(teams))
for _, team := range teams {
assert.Equal(t, orgID, team.OrgID)
ids[team.ID] = true
}
// user 2 is in team 1 and team 2 in org 3, plus should see the new visible team.
assert.True(t, ids[1], "expected to see team 1 (member)")
assert.True(t, ids[2], "expected to see team 2 (member)")
assert.True(t, ids[visible.ID], "expected to see visible team")
// user 5 is only an org member in team 1, must not see secret team 2 but must see the visible one.
teams, _, err = organization.SearchTeam(t.Context(), &organization.SearchTeamOptions{
OrgID: orgID,
UserID: 5,
IncludeVisibilities: organization.VisibleTeamVisibilitiesFor(true, true),
})
assert.NoError(t, err)
ids = make(map[int64]bool, len(teams))
for _, team := range teams {
ids[team.ID] = true
}
assert.False(t, ids[2], "user 5 must not see private team 2")
assert.True(t, ids[visible.ID], "user 5 must see the limited team")
}
func TestHasTeamRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
@@ -6,9 +6,9 @@ package organization
import (
"context"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
"gitea.dev/models/db"
"gitea.dev/models/perm"
"gitea.dev/models/unit"
)
// TeamUnit describes all units of a repository
@@ -28,19 +28,3 @@ func (t *TeamUnit) Unit() unit.Unit {
func getUnitsByTeamID(ctx context.Context, teamID int64) (units []*TeamUnit, err error) {
return units, db.GetEngine(ctx).Where("team_id = ?", teamID).Find(&units)
}
// UpdateTeamUnits updates a teams's units
func UpdateTeamUnits(ctx context.Context, team *Team, units []TeamUnit) (err error) {
return db.WithTx(ctx, func(ctx context.Context) error {
if _, err = db.GetEngine(ctx).Where("team_id = ?", team.ID).Delete(new(TeamUnit)); err != nil {
return err
}
if len(units) > 0 {
if err = db.Insert(ctx, units); err != nil {
return err
}
}
return nil
})
}
@@ -6,8 +6,8 @@ package organization
import (
"context"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"gitea.dev/models/db"
user_model "gitea.dev/models/user"
"xorm.io/builder"
)
@@ -36,14 +36,6 @@ type SearchMembersOptions struct {
TeamID int64
}
func (opts SearchMembersOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.TeamID > 0 {
cond = cond.And(builder.Eq{"": opts.TeamID})
}
return cond
}
// GetTeamMembers returns all members in given team of organization.
func GetTeamMembers(ctx context.Context, opts *SearchMembersOptions) ([]*user_model.User, error) {
var members []*user_model.User