diff --git a/api4/team.go b/api4/team.go index 5c0df4902c..da7a8f2da6 100644 --- a/api4/team.go +++ b/api4/team.go @@ -812,14 +812,31 @@ func teamExists(c *Context, w http.ResponseWriter, r *http.Request) { return } - resp := make(map[string]bool) - - if _, err := c.App.GetTeamByName(c.Params.TeamName); err != nil { - resp["exists"] = false - } else { - resp["exists"] = true + team, err := c.App.GetTeamByName(c.Params.TeamName) + if err != nil && err.StatusCode != http.StatusNotFound { + c.Err = err + return } + exists := false + + if team != nil { + var teamMember *model.TeamMember + teamMember, err = c.App.GetTeamMember(team.Id, c.App.Session.UserId) + if err != nil && err.StatusCode != http.StatusNotFound { + c.Err = err + return + } + + // Verify that the user can see the team (be a member or have the permission to list the team) + if (teamMember != nil && teamMember.DeleteAt == 0) || + (team.AllowOpenInvite && c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_LIST_PUBLIC_TEAMS)) || + (!team.AllowOpenInvite && c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_LIST_PRIVATE_TEAMS)) { + exists = true + } + } + + resp := map[string]bool{"exists": exists} w.Write([]byte(model.MapBoolToJson(resp))) } diff --git a/api4/team_test.go b/api4/team_test.go index dce8f8b7d0..7dfda587c8 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -2102,25 +2102,94 @@ func TestTeamExists(t *testing.T) { th := Setup().InitBasic() defer th.TearDown() Client := th.Client - team := th.BasicTeam + public_member_team := th.BasicTeam + err := th.App.UpdateTeamPrivacy(public_member_team.Id, model.TEAM_OPEN, true) + require.Nil(t, err) - th.LoginBasic() + public_not_member_team := th.CreateTeamWithClient(th.SystemAdminClient) + err = th.App.UpdateTeamPrivacy(public_not_member_team.Id, model.TEAM_OPEN, true) + require.Nil(t, err) - exists, resp := Client.TeamExists(team.Name, "") - CheckNoError(t, resp) - if !exists { - t.Fatal("team should exist") - } + private_member_team := th.CreateTeamWithClient(th.SystemAdminClient) + th.LinkUserToTeam(th.BasicUser, private_member_team) + err = th.App.UpdateTeamPrivacy(private_member_team.Id, model.TEAM_INVITE, false) + require.Nil(t, err) - exists, resp = Client.TeamExists("testingteam", "") - CheckNoError(t, resp) - if exists { - t.Fatal("team should not exist") - } + private_not_member_team := th.CreateTeamWithClient(th.SystemAdminClient) + err = th.App.UpdateTeamPrivacy(private_not_member_team.Id, model.TEAM_INVITE, false) + require.Nil(t, err) - Client.Logout() - _, resp = Client.TeamExists(team.Name, "") - CheckUnauthorizedStatus(t, resp) + // Check the appropriate permissions are enforced. + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + + th.AddPermissionToRole(model.PERMISSION_LIST_PUBLIC_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + th.AddPermissionToRole(model.PERMISSION_LIST_PRIVATE_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + + t.Run("Logged user with permissions and valid public team", func(t *testing.T) { + th.LoginBasic() + exists, resp := Client.TeamExists(public_not_member_team.Name, "") + CheckNoError(t, resp) + assert.True(t, exists, "team should exist") + }) + + t.Run("Logged user with permissions and valid private team", func(t *testing.T) { + th.LoginBasic() + exists, resp := Client.TeamExists(private_not_member_team.Name, "") + CheckNoError(t, resp) + assert.True(t, exists, "team should exist") + }) + + t.Run("Logged user and invalid team", func(t *testing.T) { + th.LoginBasic() + exists, resp := Client.TeamExists("testingteam", "") + CheckNoError(t, resp) + assert.False(t, exists, "team should not exist") + }) + + t.Run("Logged out user", func(t *testing.T) { + Client.Logout() + _, resp := Client.TeamExists(public_not_member_team.Name, "") + CheckUnauthorizedStatus(t, resp) + }) + + t.Run("Logged without LIST_PUBLIC_TEAMS permissions and member public team", func(t *testing.T) { + th.LoginBasic() + th.RemovePermissionFromRole(model.PERMISSION_LIST_PUBLIC_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + + exists, resp := Client.TeamExists(public_member_team.Name, "") + CheckNoError(t, resp) + assert.True(t, exists, "team should be visible") + }) + + t.Run("Logged without LIST_PUBLIC_TEAMS permissions and not member public team", func(t *testing.T) { + th.LoginBasic() + th.RemovePermissionFromRole(model.PERMISSION_LIST_PUBLIC_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + + exists, resp := Client.TeamExists(public_not_member_team.Name, "") + CheckNoError(t, resp) + assert.False(t, exists, "team should not be visible") + }) + + t.Run("Logged without LIST_PRIVATE_TEAMS permissions and member private team", func(t *testing.T) { + th.LoginBasic() + th.RemovePermissionFromRole(model.PERMISSION_LIST_PRIVATE_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + + exists, resp := Client.TeamExists(private_member_team.Name, "") + CheckNoError(t, resp) + assert.True(t, exists, "team should be visible") + }) + + t.Run("Logged without LIST_PRIVATE_TEAMS permissions and not member private team", func(t *testing.T) { + th.LoginBasic() + th.RemovePermissionFromRole(model.PERMISSION_LIST_PRIVATE_TEAMS.Id, model.SYSTEM_USER_ROLE_ID) + + exists, resp := Client.TeamExists(private_not_member_team.Name, "") + CheckNoError(t, resp) + assert.False(t, exists, "team should not be visible") + }) } func TestImportTeam(t *testing.T) { diff --git a/app/team.go b/app/team.go index 3bc3e8409e..c8e2546a39 100644 --- a/app/team.go +++ b/app/team.go @@ -643,12 +643,7 @@ func (a *App) GetTeam(teamId string) (*model.Team, *model.AppError) { } func (a *App) GetTeamByName(name string) (*model.Team, *model.AppError) { - team, err := a.Srv.Store.Team().GetByName(name) - if err != nil { - err.StatusCode = http.StatusNotFound - return nil, err - } - return team, nil + return a.Srv.Store.Team().GetByName(name) } func (a *App) GetTeamByInviteId(inviteId string) (*model.Team, *model.AppError) { diff --git a/i18n/en.json b/i18n/en.json index 3005763f68..a8a78b1019 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -6770,6 +6770,10 @@ "id": "store.sql_team.get_by_name.app_error", "translation": "Unable to find the existing team" }, + { + "id": "store.sql_team.get_by_name.missing.app_error", + "translation": "Unable to find the existing team" + }, { "id": "store.sql_team.get_by_scheme.app_error", "translation": "Unable to get the channels for the provided scheme" diff --git a/store/sqlstore/team_store.go b/store/sqlstore/team_store.go index 0de45d0f4e..b4784771f2 100644 --- a/store/sqlstore/team_store.go +++ b/store/sqlstore/team_store.go @@ -285,6 +285,9 @@ func (s SqlTeamStore) GetByName(name string) (*model.Team, *model.AppError) { err := s.GetReplica().SelectOne(&team, "SELECT * FROM Teams WHERE Name = :Name", map[string]interface{}{"Name": name}) if err != nil { + if err == sql.ErrNoRows { + return nil, model.NewAppError("SqlTeamStore.GetByName", "store.sql_team.get_by_name.missing.app_error", nil, "name="+name+","+err.Error(), http.StatusNotFound) + } return nil, model.NewAppError("SqlTeamStore.GetByName", "store.sql_team.get_by_name.app_error", nil, "name="+name+", "+err.Error(), http.StatusInternalServerError) } return &team, nil