From 98c6401f54b6cba2733317a8017193907d2582eb Mon Sep 17 00:00:00 2001 From: Doug Lauder Date: Mon, 20 Apr 2020 21:09:40 -0400 Subject: [PATCH] MM-23722 add endpoint for modify team privacy (#14287) * MM-23722 add Rest API for updating team privacy * unit tests for UpdateTeamPrivacy --- api4/team.go | 53 +++++++++++++++++++++++++++++++++++ api4/team_test.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++ model/client4.go | 12 ++++++++ 3 files changed, 135 insertions(+) diff --git a/api4/team.go b/api4/team.go index 251421bdcd..e0d899a61b 100644 --- a/api4/team.go +++ b/api4/team.go @@ -43,6 +43,7 @@ func (api *API) InitTeam() { api.BaseRoutes.Team.Handle("", api.ApiSessionRequired(updateTeam)).Methods("PUT") api.BaseRoutes.Team.Handle("", api.ApiSessionRequired(deleteTeam)).Methods("DELETE") api.BaseRoutes.Team.Handle("/patch", api.ApiSessionRequired(patchTeam)).Methods("PUT") + api.BaseRoutes.Team.Handle("/privacy", api.ApiSessionRequired(updateTeamPrivacy)).Methods("PUT") api.BaseRoutes.Team.Handle("/stats", api.ApiSessionRequired(getTeamStats)).Methods("GET") api.BaseRoutes.Team.Handle("/regenerate_invite_id", api.ApiSessionRequired(regenerateTeamInviteId)).Methods("POST") @@ -231,6 +232,58 @@ func patchTeam(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(patchedTeam.ToJson())) } +func updateTeamPrivacy(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireTeamId() + if c.Err != nil { + return + } + + props := model.StringInterfaceFromJson(r.Body) + privacy, ok := props["privacy"].(string) + if !ok { + c.SetInvalidParam("privacy") + return + } + + var openInvite bool + switch privacy { + case model.TEAM_OPEN: + openInvite = true + case model.TEAM_INVITE: + openInvite = false + default: + c.SetInvalidParam("privacy") + return + } + + auditRec := c.MakeAuditRecord("updateTeamPrivacy", audit.Fail) + defer c.LogAuditRec(auditRec) + auditRec.AddMeta("privacy", privacy) + + if !c.App.SessionHasPermissionToTeam(*c.App.Session(), c.Params.TeamId, model.PERMISSION_MANAGE_TEAM) { + auditRec.AddMeta("team_id", c.Params.TeamId) + c.SetPermissionError(model.PERMISSION_MANAGE_TEAM) + return + } + + if err := c.App.UpdateTeamPrivacy(c.Params.TeamId, privacy, openInvite); err != nil { + c.Err = err + return + } + + // Return the updated team to be consistent with UpdateChannelPrivacy + team, err := c.App.GetTeam(c.Params.TeamId) + if err != nil { + c.Err = err + return + } + + auditRec.AddMeta("team", team) + auditRec.Success() + + w.Write([]byte(team.ToJson())) +} + func regenerateTeamInviteId(c *Context, w http.ResponseWriter, r *http.Request) { c.RequireTeamId() if c.Err != nil { diff --git a/api4/team_test.go b/api4/team_test.go index f52650337e..de0ddfbc49 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -459,6 +459,76 @@ func TestPatchTeamSanitization(t *testing.T) { }) } +func TestUpdateTeamPrivacy(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + Client := th.Client + + createTeam := func(teamType string, allowOpenInvite bool) *model.Team { + team := &model.Team{ + DisplayName: teamType + " Team", + Description: "Some description", + CompanyName: "Some company name", + AllowOpenInvite: allowOpenInvite, + InviteId: model.NewId(), + Name: "aa-" + model.NewRandomTeamName() + "zz", + Email: "success+" + model.NewId() + "@simulator.amazonses.com", + Type: teamType, + } + team, _ = Client.CreateTeam(team) + return team + } + + teamPublic := createTeam(model.TEAM_OPEN, true) + teamPrivate := createTeam(model.TEAM_INVITE, false) + + teamPublic2 := createTeam(model.TEAM_OPEN, true) + teamPrivate2 := createTeam(model.TEAM_INVITE, false) + + tests := []struct { + name string + team *model.Team + privacy string + errChecker func(t *testing.T, resp *model.Response) + wantType string + wantOpenInvite bool + }{ + {name: "bad privacy", team: teamPublic, privacy: "blap", errChecker: CheckBadRequestStatus, wantType: model.TEAM_OPEN, wantOpenInvite: true}, + {name: "bad team", team: &model.Team{Id: model.NewId()}, privacy: model.TEAM_OPEN, errChecker: CheckForbiddenStatus, wantType: model.TEAM_OPEN, wantOpenInvite: true}, + {name: "public to private", team: teamPublic, privacy: model.TEAM_INVITE, errChecker: nil, wantType: model.TEAM_INVITE, wantOpenInvite: false}, + {name: "private to public", team: teamPrivate, privacy: model.TEAM_OPEN, errChecker: nil, wantType: model.TEAM_OPEN, wantOpenInvite: true}, + {name: "public to public", team: teamPublic2, privacy: model.TEAM_OPEN, errChecker: nil, wantType: model.TEAM_OPEN, wantOpenInvite: true}, + {name: "private to private", team: teamPrivate2, privacy: model.TEAM_INVITE, errChecker: nil, wantType: model.TEAM_INVITE, wantOpenInvite: false}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + team, resp := Client.UpdateTeamPrivacy(test.team.Id, test.privacy) + if test.errChecker != nil { + test.errChecker(t, resp) + return + } else { + CheckNoError(t, resp) + CheckOKStatus(t, resp) + } + require.Equal(t, test.wantType, team.Type) + require.Equal(t, test.wantOpenInvite, team.AllowOpenInvite) + }) + } + + t.Run("not logged in", func(t *testing.T) { + Client.Logout() + _, resp := Client.UpdateTeamPrivacy(teamPublic.Id, model.TEAM_INVITE) + CheckUnauthorizedStatus(t, resp) + }) + + t.Run("no permission to manage team", func(t *testing.T) { + th.LoginBasic2() + _, resp := Client.UpdateTeamPrivacy(teamPublic.Id, model.TEAM_INVITE) + CheckForbiddenStatus(t, resp) + }) +} + func TestTeamUnicodeNames(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() diff --git a/model/client4.go b/model/client4.go index 95c62908ce..0b2bcf9278 100644 --- a/model/client4.go +++ b/model/client4.go @@ -1794,6 +1794,18 @@ func (c *Client4) PermanentDeleteTeam(teamId string) (bool, *Response) { return CheckStatusOK(r), BuildResponse(r) } +// UpdateTeamPrivacy modifies the team type (model.TEAM_OPEN <--> model.TEAM_INVITE) and sets +// the corresponding AllowOpenInvite appropriately. +func (c *Client4) UpdateTeamPrivacy(teamId string, privacy string) (*Team, *Response) { + requestBody := map[string]string{"privacy": privacy} + r, err := c.DoApiPut(c.GetTeamRoute(teamId)+"/privacy", MapToJson(requestBody)) + if err != nil { + return nil, BuildErrorResponse(r, err) + } + defer closeBody(r) + return TeamFromJson(r.Body), BuildResponse(r) +} + // GetTeamMembers returns team members based on the provided team id string. func (c *Client4) GetTeamMembers(teamId string, page int, perPage int, etag string) ([]*TeamMember, *Response) { query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)