From 5a9d95d9c73078f652aaf19ef873590ec8c5d3dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 19 Mar 2019 11:36:29 +0100 Subject: [PATCH] Adding list/join public/private teams permissions (#10309) * Adding list/join public/private teams permissions * Add permission migration and allow to migrate based on role name * Adding JoinTeam new endpoint * Addressing PR review comments * Keep the previous API consistent --- api4/team.go | 44 ++- api4/team_test.go | 233 +++++++++++---- app/app_test.go | 12 + app/permissions_migrations.go | 64 +++-- app/permissions_migrations_test.go | 20 +- app/team.go | 32 ++- i18n/en.json | 8 + model/permission.go | 32 +++ model/role.go | 4 + store/sqlstore/team_store.go | 73 ++++- store/store.go | 3 + store/storetest/mocks/TeamStore.go | 48 ++++ store/storetest/team_store.go | 446 +++++++++++++++++++++-------- 13 files changed, 802 insertions(+), 217 deletions(-) diff --git a/api4/team.go b/api4/team.go index 1fed90a64c..f33baa75aa 100644 --- a/api4/team.go +++ b/api4/team.go @@ -364,9 +364,27 @@ func addTeamMember(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToTeam(c.App.Session, member.TeamId, model.PERMISSION_ADD_USER_TO_TEAM) { - c.SetPermissionError(model.PERMISSION_ADD_USER_TO_TEAM) - return + if member.UserId == c.App.Session.UserId { + var team *model.Team + team, err = c.App.GetTeam(member.TeamId) + if err != nil { + c.Err = err + return + } + + if team.AllowOpenInvite && !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_JOIN_PUBLIC_TEAMS) { + c.SetPermissionError(model.PERMISSION_JOIN_PUBLIC_TEAMS) + return + } + if !team.AllowOpenInvite && !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_JOIN_PRIVATE_TEAMS) { + c.SetPermissionError(model.PERMISSION_JOIN_PRIVATE_TEAMS) + return + } + } else { + if !c.App.SessionHasPermissionToTeam(c.App.Session, member.TeamId, model.PERMISSION_ADD_USER_TO_TEAM) { + c.SetPermissionError(model.PERMISSION_ADD_USER_TO_TEAM) + return + } } member, err = c.App.AddTeamMember(member.TeamId, member.UserId) @@ -571,10 +589,12 @@ func getAllTeams(c *Context, w http.ResponseWriter, r *http.Request) { var teams []*model.Team var err *model.AppError - if c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) { + if c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_LIST_PRIVATE_TEAMS) && c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_LIST_PUBLIC_TEAMS) { teams, err = c.App.GetAllTeamsPage(c.Params.Page*c.Params.PerPage, c.Params.PerPage) - } else { - teams, err = c.App.GetAllOpenTeamsPage(c.Params.Page*c.Params.PerPage, c.Params.PerPage) + } else if c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_LIST_PRIVATE_TEAMS) { + teams, err = c.App.GetAllPrivateTeamsPage(c.Params.Page*c.Params.PerPage, c.Params.PerPage) + } else if c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_LIST_PUBLIC_TEAMS) { + teams, err = c.App.GetAllPublicTeamsPage(c.Params.Page*c.Params.PerPage, c.Params.PerPage) } if err != nil { @@ -602,10 +622,14 @@ func searchTeams(c *Context, w http.ResponseWriter, r *http.Request) { var teams []*model.Team var err *model.AppError - if c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) { + if c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_LIST_PRIVATE_TEAMS) && c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_LIST_PUBLIC_TEAMS) { teams, err = c.App.SearchAllTeams(props.Term) + } else if c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_LIST_PRIVATE_TEAMS) { + teams, err = c.App.SearchPrivateTeams(props.Term) + } else if c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_LIST_PUBLIC_TEAMS) { + teams, err = c.App.SearchPublicTeams(props.Term) } else { - teams, err = c.App.SearchOpenTeams(props.Term) + teams = []*model.Team{} } if err != nil { @@ -752,7 +776,7 @@ func getInviteInfo(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !(team.Type == model.TEAM_OPEN) { + if team.Type != model.TEAM_OPEN { c.Err = model.NewAppError("getInviteInfo", "api.team.get_invite_info.not_open_team", nil, "id="+c.Params.InviteId, http.StatusForbidden) return } @@ -779,7 +803,7 @@ func getTeamIcon(c *Context, w http.ResponseWriter, r *http.Request) { } if !c.App.SessionHasPermissionToTeam(c.App.Session, c.Params.TeamId, model.PERMISSION_VIEW_TEAM) && - (team.Type != model.TEAM_OPEN || team.AllowOpenInvite) { + (team.Type != model.TEAM_OPEN || !team.AllowOpenInvite) { c.SetPermissionError(model.PERMISSION_VIEW_TEAM) return } diff --git a/api4/team_test.go b/api4/team_test.go index 7e67b5759a..070d1d743a 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -559,72 +559,105 @@ func TestGetAllTeams(t *testing.T) { defer th.TearDown() Client := th.Client - team := &model.Team{DisplayName: "Name", Name: GenerateTestTeamName(), Email: th.GenerateTestEmail(), Type: model.TEAM_OPEN, AllowOpenInvite: true} - _, resp := Client.CreateTeam(team) + team1 := &model.Team{DisplayName: "Name", Name: GenerateTestTeamName(), Email: th.GenerateTestEmail(), Type: model.TEAM_OPEN, AllowOpenInvite: true} + team1, resp := Client.CreateTeam(team1) CheckNoError(t, resp) team2 := &model.Team{DisplayName: "Name2", Name: GenerateTestTeamName(), Email: th.GenerateTestEmail(), Type: model.TEAM_OPEN, AllowOpenInvite: true} - _, resp = Client.CreateTeam(team2) + team2, resp = Client.CreateTeam(team2) CheckNoError(t, resp) - rrteams, resp := Client.GetAllTeams("", 0, 1) + team3 := &model.Team{DisplayName: "Name3", Name: GenerateTestTeamName(), Email: th.GenerateTestEmail(), Type: model.TEAM_OPEN, AllowOpenInvite: false} + team3, resp = Client.CreateTeam(team3) CheckNoError(t, resp) - if len(rrteams) != 1 { - t.Log(len(rrteams)) - t.Fatal("wrong number of teams - should be 1") + testCases := []struct { + Name string + Page int + PerPage int + Permissions []string + ExpectedTeams []string + }{ + { + Name: "Get 1 team per page", + Page: 0, + PerPage: 1, + Permissions: []string{model.PERMISSION_LIST_PUBLIC_TEAMS.Id}, + ExpectedTeams: []string{team1.Id}, + }, + { + Name: "Get second page with 1 team per page", + Page: 1, + PerPage: 1, + Permissions: []string{model.PERMISSION_LIST_PUBLIC_TEAMS.Id}, + ExpectedTeams: []string{team2.Id}, + }, + { + Name: "Get no items per page", + Page: 1, + PerPage: 0, + Permissions: []string{model.PERMISSION_LIST_PUBLIC_TEAMS.Id}, + ExpectedTeams: []string{}, + }, + { + Name: "Get all open teams", + Page: 0, + PerPage: 10, + Permissions: []string{model.PERMISSION_LIST_PUBLIC_TEAMS.Id}, + ExpectedTeams: []string{team1.Id, team2.Id}, + }, + { + Name: "Get all private teams", + Page: 0, + PerPage: 10, + Permissions: []string{model.PERMISSION_LIST_PRIVATE_TEAMS.Id}, + ExpectedTeams: []string{th.BasicTeam.Id, team3.Id}, + }, + { + Name: "Get all teams", + Page: 0, + PerPage: 10, + Permissions: []string{model.PERMISSION_LIST_PUBLIC_TEAMS.Id, model.PERMISSION_LIST_PRIVATE_TEAMS.Id}, + ExpectedTeams: []string{th.BasicTeam.Id, team1.Id, team2.Id, team3.Id}, + }, + { + Name: "Get no teams because permissions", + Page: 0, + PerPage: 10, + Permissions: []string{}, + ExpectedTeams: []string{}, + }, } - for _, rt := range rrteams { - if !rt.AllowOpenInvite { - t.Fatal("not all teams are open") - } + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.RemovePermissionFromRole(model.PERMISSION_LIST_PUBLIC_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_JOIN_PUBLIC_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_LIST_PRIVATE_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_JOIN_PRIVATE_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + for _, permission := range tc.Permissions { + th.AddPermissionToRole(permission, model.SYSTEM_USER_ROLE_ID) + } + + var teams []*model.Team + teams, resp = Client.GetAllTeams("", tc.Page, tc.PerPage) + CheckNoError(t, resp) + require.Equal(t, len(tc.ExpectedTeams), len(teams)) + for idx, team := range teams { + assert.Equal(t, tc.ExpectedTeams[idx], team.Id) + } + }) } - rrteams, resp = Client.GetAllTeams("", 0, 10) - CheckNoError(t, resp) - - for _, rt := range rrteams { - if !rt.AllowOpenInvite { - t.Fatal("not all teams are open") - } - } - - rrteams1, resp := Client.GetAllTeams("", 1, 0) - CheckNoError(t, resp) - - if len(rrteams1) != 0 { - t.Fatal("wrong number of teams - should be 0") - } - - rrteams2, resp := th.SystemAdminClient.GetAllTeams("", 1, 1) - CheckNoError(t, resp) - - if len(rrteams2) != 1 { - t.Fatal("wrong number of teams - should be 1") - } - - rrteams2, resp = Client.GetAllTeams("", 1, 0) - CheckNoError(t, resp) - - if len(rrteams2) != 0 { - t.Fatal("wrong number of teams - should be 0") - } - - rrteams, resp = Client.GetAllTeams("", 0, 2) - CheckNoError(t, resp) - rrteams2, resp = Client.GetAllTeams("", 1, 2) - CheckNoError(t, resp) - - for _, t1 := range rrteams { - for _, t2 := range rrteams2 { - assert.NotEqual(t, t1.Id, t2.Id, "different pages should not have the same teams") - } - } - - Client.Logout() - _, resp = Client.GetAllTeams("", 1, 10) - CheckUnauthorizedStatus(t, resp) + t.Run("Unauthorized", func(t *testing.T) { + Client.Logout() + _, resp = Client.GetAllTeams("", 1, 10) + CheckUnauthorizedStatus(t, resp) + }) } func TestGetAllTeamsSanitization(t *testing.T) { @@ -1411,6 +1444,96 @@ func TestAddTeamMember(t *testing.T) { } } +func TestAddTeamMemberMyself(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + Client := th.Client + + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + + th.LoginBasic() + + testCases := []struct { + Name string + Public bool + PublicPermission bool + PrivatePermission bool + ExpectedSuccess bool + }{ + { + Name: "Try to join an open team without the permissions", + Public: true, + PublicPermission: false, + PrivatePermission: false, + ExpectedSuccess: false, + }, + { + Name: "Try to join a private team without the permissions", + Public: false, + PublicPermission: false, + PrivatePermission: false, + ExpectedSuccess: false, + }, + { + Name: "Try to join an open team without public permission but with private permissions", + Public: true, + PublicPermission: false, + PrivatePermission: true, + ExpectedSuccess: false, + }, + { + Name: "Try to join a private team without private permission but with public permission", + Public: false, + PublicPermission: true, + PrivatePermission: false, + ExpectedSuccess: false, + }, + { + Name: "Join an open team with the permissions", + Public: true, + PublicPermission: true, + PrivatePermission: false, + ExpectedSuccess: true, + }, + { + Name: "Join a private team with the permissions", + Public: false, + PublicPermission: false, + PrivatePermission: true, + ExpectedSuccess: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + team := th.CreateTeam() + team.AllowOpenInvite = tc.Public + th.App.UpdateTeam(team) + if tc.PublicPermission { + th.AddPermissionToRole(model.PERMISSION_JOIN_PUBLIC_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + } else { + th.RemovePermissionFromRole(model.PERMISSION_JOIN_PUBLIC_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + } + if tc.PrivatePermission { + th.AddPermissionToRole(model.PERMISSION_JOIN_PRIVATE_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + } else { + th.RemovePermissionFromRole(model.PERMISSION_JOIN_PRIVATE_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + } + _, resp := Client.AddTeamMember(team.Id, th.BasicUser.Id) + if tc.ExpectedSuccess { + CheckNoError(t, resp) + } else { + CheckForbiddenStatus(t, resp) + } + }) + } + +} + func TestAddTeamMembers(t *testing.T) { th := Setup().InitBasic() defer th.TearDown() diff --git a/app/app_test.go b/app/app_test.go index 023f847fea..c1527beebc 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -121,6 +121,8 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) { model.PERMISSION_DELETE_OTHERS_POSTS.Id, }, "system_user": []string{ + model.PERMISSION_LIST_PUBLIC_TEAMS.Id, + model.PERMISSION_JOIN_PUBLIC_TEAMS.Id, model.PERMISSION_CREATE_DIRECT_CHANNEL.Id, model.PERMISSION_CREATE_GROUP_CHANNEL.Id, model.PERMISSION_PERMANENT_DELETE_USER.Id, @@ -173,6 +175,8 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) { model.PERMISSION_MANAGE_BOTS.Id, model.PERMISSION_MANAGE_OTHERS_BOTS.Id, model.PERMISSION_REMOVE_OTHERS_REACTIONS.Id, + model.PERMISSION_LIST_PRIVATE_TEAMS.Id, + model.PERMISSION_JOIN_PRIVATE_TEAMS.Id, model.PERMISSION_LIST_TEAM_CHANNELS.Id, model.PERMISSION_JOIN_PUBLIC_CHANNELS.Id, model.PERMISSION_READ_PUBLIC_CHANNEL.Id, @@ -298,6 +302,8 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) { model.PERMISSION_DELETE_OTHERS_POSTS.Id, }, "system_user": []string{ + model.PERMISSION_LIST_PUBLIC_TEAMS.Id, + model.PERMISSION_JOIN_PUBLIC_TEAMS.Id, model.PERMISSION_CREATE_DIRECT_CHANNEL.Id, model.PERMISSION_CREATE_GROUP_CHANNEL.Id, model.PERMISSION_PERMANENT_DELETE_USER.Id, @@ -350,6 +356,8 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) { model.PERMISSION_MANAGE_BOTS.Id, model.PERMISSION_MANAGE_OTHERS_BOTS.Id, model.PERMISSION_REMOVE_OTHERS_REACTIONS.Id, + model.PERMISSION_LIST_PRIVATE_TEAMS.Id, + model.PERMISSION_JOIN_PRIVATE_TEAMS.Id, model.PERMISSION_LIST_TEAM_CHANNELS.Id, model.PERMISSION_JOIN_PUBLIC_CHANNELS.Id, model.PERMISSION_READ_PUBLIC_CHANNEL.Id, @@ -491,6 +499,8 @@ func TestDoEmojisPermissionsMigration(t *testing.T) { model.PERMISSION_MANAGE_BOTS.Id, model.PERMISSION_MANAGE_OTHERS_BOTS.Id, model.PERMISSION_REMOVE_OTHERS_REACTIONS.Id, + model.PERMISSION_LIST_PRIVATE_TEAMS.Id, + model.PERMISSION_JOIN_PRIVATE_TEAMS.Id, model.PERMISSION_LIST_TEAM_CHANNELS.Id, model.PERMISSION_JOIN_PUBLIC_CHANNELS.Id, model.PERMISSION_READ_PUBLIC_CHANNEL.Id, @@ -568,6 +578,8 @@ func TestDoEmojisPermissionsMigration(t *testing.T) { role3, err3 := th.App.GetRoleByName(model.SYSTEM_USER_ROLE_ID) assert.Nil(t, err3) expected3 := []string{ + model.PERMISSION_LIST_PUBLIC_TEAMS.Id, + model.PERMISSION_JOIN_PUBLIC_TEAMS.Id, model.PERMISSION_CREATE_DIRECT_CHANNEL.Id, model.PERMISSION_CREATE_GROUP_CHANNEL.Id, model.PERMISSION_PERMANENT_DELETE_USER.Id, diff --git a/app/permissions_migrations.go b/app/permissions_migrations.go index 5f6cf00cfb..16959a8bdd 100644 --- a/app/permissions_migrations.go +++ b/app/permissions_migrations.go @@ -3,19 +3,23 @@ package app -import "github.com/mattermost/mattermost-server/model" +import ( + "github.com/mattermost/mattermost-server/model" +) type permissionTransformation struct { - On func(map[string]bool) bool + On func(string, map[string]bool) bool Add []string Remove []string } type permissionsMap []permissionTransformation const ( - MIGRATION_KEY_EMOJI_PERMISSIONS_SPLIT = "emoji_permissions_split" - MIGRATION_KEY_WEBHOOK_PERMISSIONS_SPLIT = "webhook_permissions_split" + MIGRATION_KEY_EMOJI_PERMISSIONS_SPLIT = "emoji_permissions_split" + MIGRATION_KEY_WEBHOOK_PERMISSIONS_SPLIT = "webhook_permissions_split" + MIGRATION_KEY_LIST_JOIN_PUBLIC_PRIVATE_TEAMS = "list_join_public_private_teams" + PERMISSION_MANAGE_SYSTEM = "manage_system" PERMISSION_MANAGE_EMOJIS = "manage_emojis" PERMISSION_MANAGE_OTHERS_EMOJIS = "manage_others_emojis" PERMISSION_CREATE_EMOJIS = "create_emojis" @@ -27,26 +31,36 @@ const ( PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS = "manage_others_incoming_webhooks" PERMISSION_MANAGE_OUTGOING_WEBHOOKS = "manage_outgoing_webhooks" PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS = "manage_others_outgoing_webhooks" + PERMISSION_LIST_PUBLIC_TEAMS = "list_public_teams" + PERMISSION_LIST_PRIVATE_TEAMS = "list_private_teams" + PERMISSION_JOIN_PUBLIC_TEAMS = "join_public_teams" + PERMISSION_JOIN_PRIVATE_TEAMS = "join_private_teams" ) -func permissionExists(permission string) func(map[string]bool) bool { - return func(permissions map[string]bool) bool { +func isRole(role string) func(string, map[string]bool) bool { + return func(roleName string, permissions map[string]bool) bool { + return roleName == role + } +} + +func permissionExists(permission string) func(string, map[string]bool) bool { + return func(roleName string, permissions map[string]bool) bool { val, ok := permissions[permission] return ok && val } } -func permissionNotExists(permission string) func(map[string]bool) bool { - return func(permissions map[string]bool) bool { +func permissionNotExists(permission string) func(string, map[string]bool) bool { + return func(roleName string, permissions map[string]bool) bool { val, ok := permissions[permission] return !(ok && val) } } -func permissionOr(funcs ...func(map[string]bool) bool) func(map[string]bool) bool { - return func(permissions map[string]bool) bool { +func permissionOr(funcs ...func(string, map[string]bool) bool) func(string, map[string]bool) bool { + return func(roleName string, permissions map[string]bool) bool { for _, f := range funcs { - if f(permissions) { + if f(roleName, permissions) { return true } } @@ -54,10 +68,10 @@ func permissionOr(funcs ...func(map[string]bool) bool) func(map[string]bool) boo } } -func permissionAnd(funcs ...func(map[string]bool) bool) func(map[string]bool) bool { - return func(permissions map[string]bool) bool { +func permissionAnd(funcs ...func(string, map[string]bool) bool) func(string, map[string]bool) bool { + return func(roleName string, permissions map[string]bool) bool { for _, f := range funcs { - if !f(permissions) { + if !f(roleName, permissions) { return false } } @@ -65,7 +79,7 @@ func permissionAnd(funcs ...func(map[string]bool) bool) func(map[string]bool) bo } } -func applyPermissionsMap(permissions []string, migrationMap permissionsMap) []string { +func applyPermissionsMap(roleName string, permissions []string, migrationMap permissionsMap) []string { finalMap := make(map[string]bool) var result []string for _, permission := range permissions { @@ -73,7 +87,7 @@ func applyPermissionsMap(permissions []string, migrationMap permissionsMap) []st } for _, transformation := range migrationMap { - if transformation.On(finalMap) { + if transformation.On(roleName, finalMap) { for _, add := range transformation.Add { finalMap[add] = true } @@ -102,7 +116,7 @@ func (a *App) doPermissionsMigration(key string, migrationMap permissionsMap) *m } for _, role := range roles { - role.Permissions = applyPermissionsMap(role.Permissions, migrationMap) + role.Permissions = applyPermissionsMap(role.Name, role.Permissions, migrationMap) if result := <-a.Srv.Store.Role().Save(role); result.Err != nil { return result.Err } @@ -144,6 +158,21 @@ func getWebhooksPermissionsSplitMigration() permissionsMap { } } +func getListJoinPublicPrivateTeamsPermissionsMigration() permissionsMap { + return permissionsMap{ + permissionTransformation{ + On: isRole(model.SYSTEM_ADMIN_ROLE_ID), + Add: []string{PERMISSION_LIST_PRIVATE_TEAMS, PERMISSION_JOIN_PRIVATE_TEAMS}, + Remove: []string{}, + }, + permissionTransformation{ + On: isRole(model.SYSTEM_USER_ROLE_ID), + Add: []string{PERMISSION_LIST_PUBLIC_TEAMS, PERMISSION_JOIN_PUBLIC_TEAMS}, + Remove: []string{}, + }, + } +} + // DoPermissionsMigrations execute all the permissions migrations need by the current version. func (a *App) DoPermissionsMigrations() *model.AppError { PermissionsMigrations := []struct { @@ -152,6 +181,7 @@ func (a *App) DoPermissionsMigrations() *model.AppError { }{ {Key: MIGRATION_KEY_EMOJI_PERMISSIONS_SPLIT, Migration: getEmojisPermissionsSplitMigration}, {Key: MIGRATION_KEY_WEBHOOK_PERMISSIONS_SPLIT, Migration: getWebhooksPermissionsSplitMigration}, + {Key: MIGRATION_KEY_LIST_JOIN_PUBLIC_PRIVATE_TEAMS, Migration: getListJoinPublicPrivateTeamsPermissionsMigration}, } for _, migration := range PermissionsMigrations { diff --git a/app/permissions_migrations_test.go b/app/permissions_migrations_test.go index d7ece4df3c..80a45e42b7 100644 --- a/app/permissions_migrations_test.go +++ b/app/permissions_migrations_test.go @@ -77,11 +77,29 @@ func TestApplyPermissionsMap(t *testing.T) { }}, []string{"test1", "test2", "test3"}, }, + { + "When the role matches", + []string{"test1", "test2", "test3"}, + permissionsMap{permissionTransformation{ + On: isRole("system_admin"), + Add: []string{"test4"}, + }}, + []string{"test1", "test2", "test3", "test4"}, + }, + { + "When the role doesn't match", + []string{"test1", "test2", "test3"}, + permissionsMap{permissionTransformation{ + On: isRole("system_user"), + Add: []string{"test4"}, + }}, + []string{"test1", "test2", "test3"}, + }, } for _, tc := range tt { t.Run(tc.Name, func(t *testing.T) { - result := applyPermissionsMap(tc.Permissions, tc.TranslationMap) + result := applyPermissionsMap("system_admin", tc.Permissions, tc.TranslationMap) sort.Strings(result) assert.Equal(t, tc.ExpectedResult, result) }) diff --git a/app/team.go b/app/team.go index 1fca8d283c..a78599c35d 100644 --- a/app/team.go +++ b/app/team.go @@ -562,7 +562,23 @@ func (a *App) GetAllTeamsPage(offset int, limit int) ([]*model.Team, *model.AppE return result.Data.([]*model.Team), nil } -func (a *App) GetAllOpenTeams() ([]*model.Team, *model.AppError) { +func (a *App) GetAllPrivateTeams() ([]*model.Team, *model.AppError) { + result := <-a.Srv.Store.Team().GetAllPrivateTeamListing() + if result.Err != nil { + return nil, result.Err + } + return result.Data.([]*model.Team), nil +} + +func (a *App) GetAllPrivateTeamsPage(offset int, limit int) ([]*model.Team, *model.AppError) { + result := <-a.Srv.Store.Team().GetAllPrivateTeamPageListing(offset, limit) + if result.Err != nil { + return nil, result.Err + } + return result.Data.([]*model.Team), nil +} + +func (a *App) GetAllPublicTeams() ([]*model.Team, *model.AppError) { result := <-a.Srv.Store.Team().GetAllTeamListing() if result.Err != nil { return nil, result.Err @@ -570,6 +586,14 @@ func (a *App) GetAllOpenTeams() ([]*model.Team, *model.AppError) { return result.Data.([]*model.Team), nil } +func (a *App) GetAllPublicTeamsPage(offset int, limit int) ([]*model.Team, *model.AppError) { + result := <-a.Srv.Store.Team().GetAllTeamPageListing(offset, limit) + if result.Err != nil { + return nil, result.Err + } + return result.Data.([]*model.Team), nil +} + func (a *App) SearchAllTeams(term string) ([]*model.Team, *model.AppError) { result := <-a.Srv.Store.Team().SearchAll(term) if result.Err != nil { @@ -578,7 +602,7 @@ func (a *App) SearchAllTeams(term string) ([]*model.Team, *model.AppError) { return result.Data.([]*model.Team), nil } -func (a *App) SearchOpenTeams(term string) ([]*model.Team, *model.AppError) { +func (a *App) SearchPublicTeams(term string) ([]*model.Team, *model.AppError) { result := <-a.Srv.Store.Team().SearchOpen(term) if result.Err != nil { return nil, result.Err @@ -586,8 +610,8 @@ func (a *App) SearchOpenTeams(term string) ([]*model.Team, *model.AppError) { return result.Data.([]*model.Team), nil } -func (a *App) GetAllOpenTeamsPage(offset int, limit int) ([]*model.Team, *model.AppError) { - result := <-a.Srv.Store.Team().GetAllTeamPageListing(offset, limit) +func (a *App) SearchPrivateTeams(term string) ([]*model.Team, *model.AppError) { + result := <-a.Srv.Store.Team().SearchPrivate(term) if result.Err != nil { return nil, result.Err } diff --git a/i18n/en.json b/i18n/en.json index 4ec32f3caa..251593356b 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -6250,6 +6250,10 @@ "id": "store.sql_team.get_all.app_error", "translation": "We could not get all teams" }, + { + "id": "store.sql_team.get_all_private_team_listing.app_error", + "translation": "We could not get all private teams" + }, { "id": "store.sql_team.get_all_team_listing.app_error", "translation": "We could not get all teams" @@ -6354,6 +6358,10 @@ "id": "store.sql_team.search_open_team.app_error", "translation": "We encountered an error searching open teams" }, + { + "id": "store.sql_team.search_private_team.app_error", + "translation": "We encountered an error searching private teams" + }, { "id": "store.sql_team.update.app_error", "translation": "Unable to update the team" diff --git a/model/permission.go b/model/permission.go index 2d7ca4c682..fdc3a180e8 100644 --- a/model/permission.go +++ b/model/permission.go @@ -33,6 +33,10 @@ var PERMISSION_CREATE_DIRECT_CHANNEL *Permission var PERMISSION_CREATE_GROUP_CHANNEL *Permission var PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES *Permission var PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES *Permission +var PERMISSION_LIST_PUBLIC_TEAMS *Permission +var PERMISSION_JOIN_PUBLIC_TEAMS *Permission +var PERMISSION_LIST_PRIVATE_TEAMS *Permission +var PERMISSION_JOIN_PRIVATE_TEAMS *Permission var PERMISSION_LIST_TEAM_CHANNELS *Permission var PERMISSION_JOIN_PUBLIC_CHANNELS *Permission var PERMISSION_DELETE_PUBLIC_CHANNEL *Permission @@ -198,6 +202,30 @@ func initializePermissions() { "authentication.permissions.manage_private_channel_properties.description", PERMISSION_SCOPE_CHANNEL, } + PERMISSION_LIST_PUBLIC_TEAMS = &Permission{ + "list_public_teams", + "authentication.permissions.list_public_teams.name", + "authentication.permissions.list_public_teams.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_JOIN_PUBLIC_TEAMS = &Permission{ + "join_public_teams", + "authentication.permissions.join_public_teams.name", + "authentication.permissions.join_public_teams.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_LIST_PRIVATE_TEAMS = &Permission{ + "list_private_teams", + "authentication.permissions.list_private_teams.name", + "authentication.permissions.list_private_teams.description", + PERMISSION_SCOPE_SYSTEM, + } + PERMISSION_JOIN_PRIVATE_TEAMS = &Permission{ + "join_private_teams", + "authentication.permissions.join_private_teams.name", + "authentication.permissions.join_private_teams.description", + PERMISSION_SCOPE_SYSTEM, + } PERMISSION_LIST_TEAM_CHANNELS = &Permission{ "list_team_channels", "authentication.permissions.list_team_channels.name", @@ -505,6 +533,10 @@ func initializePermissions() { PERMISSION_CREATE_GROUP_CHANNEL, PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES, PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES, + PERMISSION_LIST_PUBLIC_TEAMS, + PERMISSION_JOIN_PUBLIC_TEAMS, + PERMISSION_LIST_PRIVATE_TEAMS, + PERMISSION_JOIN_PRIVATE_TEAMS, PERMISSION_LIST_TEAM_CHANNELS, PERMISSION_JOIN_PUBLIC_CHANNELS, PERMISSION_DELETE_PUBLIC_CHANNEL, diff --git a/model/role.go b/model/role.go index df0eec3e3f..aa255b5835 100644 --- a/model/role.go +++ b/model/role.go @@ -264,6 +264,8 @@ func MakeDefaultRoles() map[string]*Role { DisplayName: "authentication.roles.global_user.name", Description: "authentication.roles.global_user.description", Permissions: []string{ + PERMISSION_LIST_PUBLIC_TEAMS.Id, + PERMISSION_JOIN_PUBLIC_TEAMS.Id, PERMISSION_CREATE_DIRECT_CHANNEL.Id, PERMISSION_CREATE_GROUP_CHANNEL.Id, PERMISSION_PERMANENT_DELETE_USER.Id, @@ -354,6 +356,8 @@ func MakeDefaultRoles() map[string]*Role { PERMISSION_MANAGE_BOTS.Id, PERMISSION_MANAGE_OTHERS_BOTS.Id, PERMISSION_REMOVE_OTHERS_REACTIONS.Id, + PERMISSION_LIST_PRIVATE_TEAMS.Id, + PERMISSION_JOIN_PRIVATE_TEAMS.Id, }, roles[TEAM_USER_ROLE_ID].Permissions..., ), diff --git a/store/sqlstore/team_store.go b/store/sqlstore/team_store.go index fe29628d68..1f6ddebabe 100644 --- a/store/sqlstore/team_store.go +++ b/store/sqlstore/team_store.go @@ -341,10 +341,23 @@ func (s SqlTeamStore) SearchOpen(term string) store.StoreChannel { }) } +func (s SqlTeamStore) SearchPrivate(term string) store.StoreChannel { + return store.Do(func(result *store.StoreResult) { + var teams []*model.Team + + if _, err := s.GetReplica().Select(&teams, "SELECT * FROM Teams WHERE (Type != 'O' OR AllowOpenInvite = false) AND (Name LIKE :Term OR DisplayName LIKE :Term)", map[string]interface{}{"Term": term + "%"}); err != nil { + result.Err = model.NewAppError("SqlTeamStore.SearchPrivate", "store.sql_team.search_private_team.app_error", nil, "term="+term+", "+err.Error(), http.StatusInternalServerError) + return + } + + result.Data = teams + }) +} + func (s SqlTeamStore) GetAll() store.StoreChannel { return store.Do(func(result *store.StoreResult) { var data []*model.Team - if _, err := s.GetReplica().Select(&data, "SELECT * FROM Teams"); err != nil { + if _, err := s.GetReplica().Select(&data, "SELECT * FROM Teams ORDER BY DisplayName"); err != nil { result.Err = model.NewAppError("SqlTeamStore.GetAllTeams", "store.sql_team.get_all.app_error", nil, err.Error(), http.StatusInternalServerError) return } @@ -395,12 +408,60 @@ func (s SqlTeamStore) GetTeamsByUserId(userId string) store.StoreChannel { }) } -func (s SqlTeamStore) GetAllTeamListing() store.StoreChannel { +func (s SqlTeamStore) GetAllPrivateTeamListing() store.StoreChannel { return store.Do(func(result *store.StoreResult) { - query := "SELECT * FROM Teams WHERE AllowOpenInvite = 1" + query := "SELECT * FROM Teams WHERE AllowOpenInvite = 0 ORDER BY DisplayName" if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { - query = "SELECT * FROM Teams WHERE AllowOpenInvite = true" + query = "SELECT * FROM Teams WHERE AllowOpenInvite = false ORDER BY DisplayName" + } + + var data []*model.Team + if _, err := s.GetReplica().Select(&data, query); err != nil { + result.Err = model.NewAppError("SqlTeamStore.GetAllPrivateTeamListing", "store.sql_team.get_all_private_team_listing.app_error", nil, err.Error(), http.StatusInternalServerError) + return + } + + for _, team := range data { + if len(team.InviteId) == 0 { + team.InviteId = team.Id + } + } + + result.Data = data + }) +} + +func (s SqlTeamStore) GetAllPrivateTeamPageListing(offset int, limit int) store.StoreChannel { + return store.Do(func(result *store.StoreResult) { + query := "SELECT * FROM Teams WHERE AllowOpenInvite = 0 ORDER BY DisplayName LIMIT :Limit OFFSET :Offset" + + if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { + query = "SELECT * FROM Teams WHERE AllowOpenInvite = false ORDER BY DisplayName LIMIT :Limit OFFSET :Offset" + } + + var data []*model.Team + if _, err := s.GetReplica().Select(&data, query, map[string]interface{}{"Offset": offset, "Limit": limit}); err != nil { + result.Err = model.NewAppError("SqlTeamStore.GetAllPrivateTeamListing", "store.sql_team.get_all_private_team_listing.app_error", nil, err.Error(), http.StatusInternalServerError) + return + } + + for _, team := range data { + if len(team.InviteId) == 0 { + team.InviteId = team.Id + } + } + + result.Data = data + }) +} + +func (s SqlTeamStore) GetAllTeamListing() store.StoreChannel { + return store.Do(func(result *store.StoreResult) { + query := "SELECT * FROM Teams WHERE AllowOpenInvite = 1 ORDER BY DisplayName" + + if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { + query = "SELECT * FROM Teams WHERE AllowOpenInvite = true ORDER BY DisplayName" } var data []*model.Team @@ -421,10 +482,10 @@ func (s SqlTeamStore) GetAllTeamListing() store.StoreChannel { func (s SqlTeamStore) GetAllTeamPageListing(offset int, limit int) store.StoreChannel { return store.Do(func(result *store.StoreResult) { - query := "SELECT * FROM Teams WHERE AllowOpenInvite = 1 LIMIT :Limit OFFSET :Offset" + query := "SELECT * FROM Teams WHERE AllowOpenInvite = 1 ORDER BY DisplayName LIMIT :Limit OFFSET :Offset" if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { - query = "SELECT * FROM Teams WHERE AllowOpenInvite = true LIMIT :Limit OFFSET :Offset" + query = "SELECT * FROM Teams WHERE AllowOpenInvite = true ORDER BY DisplayName LIMIT :Limit OFFSET :Offset" } var data []*model.Team diff --git a/store/store.go b/store/store.go index 9bcc6c97c9..c61d62a8c9 100644 --- a/store/store.go +++ b/store/store.go @@ -89,8 +89,11 @@ type TeamStore interface { SearchByName(name string) StoreChannel SearchAll(term string) StoreChannel SearchOpen(term string) StoreChannel + SearchPrivate(term string) StoreChannel GetAll() StoreChannel GetAllPage(offset int, limit int) StoreChannel + GetAllPrivateTeamListing() StoreChannel + GetAllPrivateTeamPageListing(offset int, limit int) StoreChannel GetAllTeamListing() StoreChannel GetAllTeamPageListing(offset int, limit int) StoreChannel GetTeamsByUserId(userId string) StoreChannel diff --git a/store/storetest/mocks/TeamStore.go b/store/storetest/mocks/TeamStore.go index abcaffcf82..be1bec4019 100644 --- a/store/storetest/mocks/TeamStore.go +++ b/store/storetest/mocks/TeamStore.go @@ -141,6 +141,38 @@ func (_m *TeamStore) GetAllPage(offset int, limit int) store.StoreChannel { return r0 } +// GetAllPrivateTeamListing provides a mock function with given fields: +func (_m *TeamStore) GetAllPrivateTeamListing() store.StoreChannel { + ret := _m.Called() + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func() store.StoreChannel); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} + +// GetAllPrivateTeamPageListing provides a mock function with given fields: offset, limit +func (_m *TeamStore) GetAllPrivateTeamPageListing(offset int, limit int) store.StoreChannel { + ret := _m.Called(offset, limit) + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func(int, int) store.StoreChannel); ok { + r0 = rf(offset, limit) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} + // GetAllTeamListing provides a mock function with given fields: func (_m *TeamStore) GetAllTeamListing() store.StoreChannel { ret := _m.Called() @@ -557,6 +589,22 @@ func (_m *TeamStore) SearchOpen(term string) store.StoreChannel { return r0 } +// SearchPrivate provides a mock function with given fields: term +func (_m *TeamStore) SearchPrivate(term string) store.StoreChannel { + ret := _m.Called(term) + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok { + r0 = rf(term) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} + // Update provides a mock function with given fields: team func (_m *TeamStore) Update(team *model.Team) store.StoreChannel { ret := _m.Called(team) diff --git a/store/storetest/team_store.go b/store/storetest/team_store.go index 307c4ed108..29bf72fa24 100644 --- a/store/storetest/team_store.go +++ b/store/storetest/team_store.go @@ -26,10 +26,13 @@ func TestTeamStore(t *testing.T, ss store.Store) { t.Run("SearchByName", func(t *testing.T) { testTeamStoreSearchByName(t, ss) }) t.Run("SearchAll", func(t *testing.T) { testTeamStoreSearchAll(t, ss) }) t.Run("SearchOpen", func(t *testing.T) { testTeamStoreSearchOpen(t, ss) }) + t.Run("SearchPrivate", func(t *testing.T) { testTeamStoreSearchPrivate(t, ss) }) t.Run("GetByIniviteId", func(t *testing.T) { testTeamStoreGetByIniviteId(t, ss) }) t.Run("ByUserId", func(t *testing.T) { testTeamStoreByUserId(t, ss) }) t.Run("GetAllTeamListing", func(t *testing.T) { testGetAllTeamListing(t, ss) }) t.Run("GetAllTeamPageListing", func(t *testing.T) { testGetAllTeamPageListing(t, ss) }) + t.Run("GetAllPrivateTeamListing", func(t *testing.T) { testGetAllPrivateTeamListing(t, ss) }) + t.Run("GetAllPrivateTeamPageListing", func(t *testing.T) { testGetAllPrivateTeamPageListing(t, ss) }) t.Run("Delete", func(t *testing.T) { testDelete(t, ss) }) t.Run("TeamCount", func(t *testing.T) { testTeamCount(t, ss) }) t.Run("TeamMembers", func(t *testing.T) { testTeamMembers(t, ss) }) @@ -185,143 +188,200 @@ func testTeamStoreSearchByName(t *testing.T, ss store.Store) { } func testTeamStoreSearchAll(t *testing.T, ss store.Store) { - o1 := model.Team{} - o1.DisplayName = "ADisplayName" + model.NewId() - o1.Name = "zz" + model.NewId() + "a" - o1.Email = MakeEmail() - o1.Type = model.TEAM_OPEN + o := model.Team{} + o.DisplayName = "ADisplayName" + model.NewId() + o.Name = "zzzzzz-" + model.NewId() + "a" + o.Email = MakeEmail() + o.Type = model.TEAM_OPEN + o.AllowOpenInvite = true - if err := (<-ss.Team().Save(&o1)).Err; err != nil { - t.Fatal(err) + require.Nil(t, (<-ss.Team().Save(&o)).Err) + + p := model.Team{} + p.DisplayName = "ADisplayName" + model.NewId() + p.Name = "zzzzzz-" + model.NewId() + "a" + p.Email = MakeEmail() + p.Type = model.TEAM_OPEN + p.AllowOpenInvite = false + + require.Nil(t, (<-ss.Team().Save(&p)).Err) + + testCases := []struct { + Name string + Term string + ExpectedLenth int + ExpectedFirstId string + }{ + { + "Search for open team name", + o.Name, + 1, + o.Id, + }, + { + "Search for open team displayName", + o.DisplayName, + 1, + o.Id, + }, + { + "Search for open team without results", + "junk", + 0, + "", + }, + { + "Search for private team", + p.DisplayName, + 1, + p.Id, + }, + { + "Search for both teams", + "zzzzzz", + 2, + "", + }, } - p2 := model.Team{} - p2.DisplayName = "BDisplayName" + model.NewId() - p2.Name = "b" + model.NewId() + "b" - p2.Email = MakeEmail() - p2.Type = model.TEAM_INVITE - - if err := (<-ss.Team().Save(&p2)).Err; err != nil { - t.Fatal(err) - } - - r1 := <-ss.Team().SearchAll(o1.Name) - if r1.Err != nil { - t.Fatal(r1.Err) - } - if len(r1.Data.([]*model.Team)) != 1 { - t.Fatal("should have returned 1 team") - } - if r1.Data.([]*model.Team)[0].ToJson() != o1.ToJson() { - t.Fatal("invalid returned team") - } - - r1 = <-ss.Team().SearchAll(p2.DisplayName) - if r1.Err != nil { - t.Fatal(r1.Err) - } - if len(r1.Data.([]*model.Team)) != 1 { - t.Fatal("should have returned 1 team") - } - if r1.Data.([]*model.Team)[0].ToJson() != p2.ToJson() { - t.Fatal("invalid returned team") - } - - r1 = <-ss.Team().SearchAll("junk") - if r1.Err != nil { - t.Fatal(r1.Err) - } - if len(r1.Data.([]*model.Team)) != 0 { - t.Fatal("should have not returned a team") + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + r1 := <-ss.Team().SearchAll(tc.Term) + require.Nil(t, r1.Err) + results := r1.Data.([]*model.Team) + require.Equal(t, tc.ExpectedLenth, len(results)) + if tc.ExpectedFirstId != "" { + assert.Equal(t, tc.ExpectedFirstId, results[0].Id) + } + }) } } func testTeamStoreSearchOpen(t *testing.T, ss store.Store) { - o1 := model.Team{} - o1.DisplayName = "ADisplayName" + model.NewId() - o1.Name = "zz" + model.NewId() + "a" - o1.Email = MakeEmail() - o1.Type = model.TEAM_OPEN - o1.AllowOpenInvite = true + o := model.Team{} + o.DisplayName = "ADisplayName" + model.NewId() + o.Name = "zz" + model.NewId() + "a" + o.Email = MakeEmail() + o.Type = model.TEAM_OPEN + o.AllowOpenInvite = true - if err := (<-ss.Team().Save(&o1)).Err; err != nil { - t.Fatal(err) + require.Nil(t, (<-ss.Team().Save(&o)).Err) + + p := model.Team{} + p.DisplayName = "ADisplayName" + model.NewId() + p.Name = "zz" + model.NewId() + "a" + p.Email = MakeEmail() + p.Type = model.TEAM_OPEN + p.AllowOpenInvite = false + + require.Nil(t, (<-ss.Team().Save(&p)).Err) + + testCases := []struct { + Name string + Term string + ExpectedLenth int + ExpectedFirstId string + }{ + { + "Search for open team name", + o.Name, + 1, + o.Id, + }, + { + "Search for open team displayName", + o.DisplayName, + 1, + o.Id, + }, + { + "Search for open team without results", + "junk", + 0, + "", + }, + { + "Search for a private team (expected no results)", + p.DisplayName, + 0, + "", + }, } - o2 := model.Team{} - o2.DisplayName = "ADisplayName" + model.NewId() - o2.Name = "zz" + model.NewId() + "a" - o2.Email = MakeEmail() - o2.Type = model.TEAM_OPEN - o2.AllowOpenInvite = false + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + r1 := <-ss.Team().SearchOpen(tc.Term) + require.Nil(t, r1.Err) + results := r1.Data.([]*model.Team) + require.Equal(t, tc.ExpectedLenth, len(results)) + if tc.ExpectedFirstId != "" { + assert.Equal(t, tc.ExpectedFirstId, results[0].Id) + } + }) + } +} - if err := (<-ss.Team().Save(&o2)).Err; err != nil { - t.Fatal(err) +func testTeamStoreSearchPrivate(t *testing.T, ss store.Store) { + o := model.Team{} + o.DisplayName = "ADisplayName" + model.NewId() + o.Name = "zz" + model.NewId() + "a" + o.Email = MakeEmail() + o.Type = model.TEAM_OPEN + o.AllowOpenInvite = true + + require.Nil(t, (<-ss.Team().Save(&o)).Err) + + p := model.Team{} + p.DisplayName = "ADisplayName" + model.NewId() + p.Name = "zz" + model.NewId() + "a" + p.Email = MakeEmail() + p.Type = model.TEAM_OPEN + p.AllowOpenInvite = false + + require.Nil(t, (<-ss.Team().Save(&p)).Err) + + testCases := []struct { + Name string + Term string + ExpectedLenth int + ExpectedFirstId string + }{ + { + "Search for private team name", + p.Name, + 1, + p.Id, + }, + { + "Search for private team displayName", + p.DisplayName, + 1, + p.Id, + }, + { + "Search for private team without results", + "junk", + 0, + "", + }, + { + "Search for a open team (expected no results)", + o.DisplayName, + 0, + "", + }, } - p2 := model.Team{} - p2.DisplayName = "BDisplayName" + model.NewId() - p2.Name = "b" + model.NewId() + "b" - p2.Email = MakeEmail() - p2.Type = model.TEAM_INVITE - p2.AllowOpenInvite = true - - if err := (<-ss.Team().Save(&p2)).Err; err != nil { - t.Fatal(err) - } - - r1 := <-ss.Team().SearchOpen(o1.Name) - if r1.Err != nil { - t.Fatal(r1.Err) - } - if len(r1.Data.([]*model.Team)) != 1 { - t.Fatal("should have returned 1 team") - } - if r1.Data.([]*model.Team)[0].ToJson() != o1.ToJson() { - t.Fatal("invalid returned team") - } - - r1 = <-ss.Team().SearchOpen(o1.DisplayName) - if r1.Err != nil { - t.Fatal(r1.Err) - } - if len(r1.Data.([]*model.Team)) != 1 { - t.Fatal("should have returned 1 team") - } - if r1.Data.([]*model.Team)[0].ToJson() != o1.ToJson() { - t.Fatal("invalid returned team") - } - - r1 = <-ss.Team().SearchOpen(p2.Name) - if r1.Err != nil { - t.Fatal(r1.Err) - } - if len(r1.Data.([]*model.Team)) != 0 { - t.Fatal("should have not returned a team") - } - - r1 = <-ss.Team().SearchOpen(p2.DisplayName) - if r1.Err != nil { - t.Fatal(r1.Err) - } - if len(r1.Data.([]*model.Team)) != 0 { - t.Fatal("should have not returned a team") - } - - r1 = <-ss.Team().SearchOpen("junk") - if r1.Err != nil { - t.Fatal(r1.Err) - } - if len(r1.Data.([]*model.Team)) != 0 { - t.Fatal("should have not returned a team") - } - - r1 = <-ss.Team().SearchOpen(o2.DisplayName) - if r1.Err != nil { - t.Fatal(r1.Err) - } - if len(r1.Data.([]*model.Team)) != 0 { - t.Fatal("should have not returned a team") + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + r1 := <-ss.Team().SearchPrivate(tc.Term) + require.Nil(t, r1.Err) + results := r1.Data.([]*model.Team) + require.Equal(t, tc.ExpectedLenth, len(results)) + if tc.ExpectedFirstId != "" { + assert.Equal(t, tc.ExpectedFirstId, results[0].Id) + } + }) } } @@ -536,6 +596,144 @@ func testGetAllTeamPageListing(t *testing.T, ss store.Store) { } } +func testGetAllPrivateTeamListing(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "DisplayName" + o1.Name = "z-z-z" + model.NewId() + "b" + o1.Email = MakeEmail() + o1.Type = model.TEAM_OPEN + o1.AllowOpenInvite = true + store.Must(ss.Team().Save(&o1)) + + o2 := model.Team{} + o2.DisplayName = "DisplayName" + o2.Name = "zz" + model.NewId() + "b" + o2.Email = MakeEmail() + o2.Type = model.TEAM_OPEN + store.Must(ss.Team().Save(&o2)) + + o3 := model.Team{} + o3.DisplayName = "DisplayName" + o3.Name = "z-z-z" + model.NewId() + "b" + o3.Email = MakeEmail() + o3.Type = model.TEAM_INVITE + o3.AllowOpenInvite = true + store.Must(ss.Team().Save(&o3)) + + o4 := model.Team{} + o4.DisplayName = "DisplayName" + o4.Name = "zz" + model.NewId() + "b" + o4.Email = MakeEmail() + o4.Type = model.TEAM_INVITE + store.Must(ss.Team().Save(&o4)) + + if r1 := <-ss.Team().GetAllPrivateTeamListing(); r1.Err != nil { + t.Fatal(r1.Err) + } else { + teams := r1.Data.([]*model.Team) + + for _, team := range teams { + if team.AllowOpenInvite { + t.Fatal("should have returned team with AllowOpenInvite as false") + } + } + + if len(teams) == 0 { + t.Fatal("failed team listing") + } + } +} + +func testGetAllPrivateTeamPageListing(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "DisplayName" + o1.Name = "z-z-z" + model.NewId() + "b" + o1.Email = MakeEmail() + o1.Type = model.TEAM_OPEN + o1.AllowOpenInvite = true + store.Must(ss.Team().Save(&o1)) + + o2 := model.Team{} + o2.DisplayName = "DisplayName" + o2.Name = "zz" + model.NewId() + "b" + o2.Email = MakeEmail() + o2.Type = model.TEAM_OPEN + o2.AllowOpenInvite = false + store.Must(ss.Team().Save(&o2)) + + o3 := model.Team{} + o3.DisplayName = "DisplayName" + o3.Name = "z-z-z" + model.NewId() + "b" + o3.Email = MakeEmail() + o3.Type = model.TEAM_INVITE + o3.AllowOpenInvite = true + store.Must(ss.Team().Save(&o3)) + + o4 := model.Team{} + o4.DisplayName = "DisplayName" + o4.Name = "zz" + model.NewId() + "b" + o4.Email = MakeEmail() + o4.Type = model.TEAM_INVITE + o4.AllowOpenInvite = false + store.Must(ss.Team().Save(&o4)) + + if r1 := <-ss.Team().GetAllPrivateTeamPageListing(0, 10); r1.Err != nil { + t.Fatal(r1.Err) + } else { + teams := r1.Data.([]*model.Team) + + for _, team := range teams { + if team.AllowOpenInvite { + t.Fatal("should have returned team with AllowOpenInvite as false") + } + } + + if len(teams) > 10 { + t.Fatal("should have returned max of 10 teams") + } + } + + o5 := model.Team{} + o5.DisplayName = "DisplayName" + o5.Name = "z-z-z" + model.NewId() + "b" + o5.Email = MakeEmail() + o5.Type = model.TEAM_OPEN + o5.AllowOpenInvite = true + store.Must(ss.Team().Save(&o5)) + + if r1 := <-ss.Team().GetAllPrivateTeamPageListing(0, 4); r1.Err != nil { + t.Fatal(r1.Err) + } else { + teams := r1.Data.([]*model.Team) + + for _, team := range teams { + if team.AllowOpenInvite { + t.Fatal("should have returned team with AllowOpenInvite as false") + } + } + + if len(teams) > 4 { + t.Fatal("should have returned max of 4 teams") + } + } + + if r1 := <-ss.Team().GetAllPrivateTeamPageListing(1, 1); r1.Err != nil { + t.Fatal(r1.Err) + } else { + teams := r1.Data.([]*model.Team) + + for _, team := range teams { + if team.AllowOpenInvite { + t.Fatal("should have returned team with AllowOpenInvite as false") + } + } + + if len(teams) > 1 { + t.Fatal("should have returned max of 1 team") + } + } +} + func testDelete(t *testing.T, ss store.Store) { o1 := model.Team{} o1.DisplayName = "DisplayName"