From a71a9fc3bff1b6a6c9d5e0a65f53686922572834 Mon Sep 17 00:00:00 2001 From: Saturnino Abril Date: Tue, 14 Mar 2017 21:08:58 +0900 Subject: APIv4 DELETE channels/{channel_id} (#5723) --- api4/apitestlib.go | 46 ++++++++++ api4/channel.go | 35 ++++++++ api4/channel_test.go | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++ app/channel.go | 3 + i18n/en.json | 4 + model/client4.go | 10 +++ 6 files changed, 337 insertions(+) diff --git a/api4/apitestlib.go b/api4/apitestlib.go index 2b7b707c7..fbad3d6be 100644 --- a/api4/apitestlib.go +++ b/api4/apitestlib.go @@ -572,3 +572,49 @@ func cleanupTestFile(info *model.FileInfo) error { return nil } + +func MakeUserChannelAdmin(user *model.User, channel *model.Channel) { + utils.DisableDebugLogForTest() + + if cmr := <-app.Srv.Store.Channel().GetMember(channel.Id, user.Id); cmr.Err == nil { + cm := cmr.Data.(*model.ChannelMember) + cm.Roles = "channel_admin channel_user" + if sr := <-app.Srv.Store.Channel().UpdateMember(cm); sr.Err != nil { + utils.EnableDebugLogForTest() + panic(sr.Err) + } + } else { + utils.EnableDebugLogForTest() + panic(cmr.Err) + } + + utils.EnableDebugLogForTest() +} + +func UpdateUserToTeamAdmin(user *model.User, team *model.Team) { + utils.DisableDebugLogForTest() + + tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.ROLE_TEAM_USER.Id + " " + model.ROLE_TEAM_ADMIN.Id} + if tmr := <-app.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil { + utils.EnableDebugLogForTest() + l4g.Error(tmr.Err.Error()) + l4g.Close() + time.Sleep(time.Second) + panic(tmr.Err) + } + utils.EnableDebugLogForTest() +} + +func UpdateUserToNonTeamAdmin(user *model.User, team *model.Team) { + utils.DisableDebugLogForTest() + + tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.ROLE_TEAM_USER.Id} + if tmr := <-app.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil { + utils.EnableDebugLogForTest() + l4g.Error(tmr.Err.Error()) + l4g.Close() + time.Sleep(time.Second) + panic(tmr.Err) + } + utils.EnableDebugLogForTest() +} diff --git a/api4/channel.go b/api4/channel.go index 80ae9fa30..acf14846a 100644 --- a/api4/channel.go +++ b/api4/channel.go @@ -22,6 +22,7 @@ func InitChannel() { BaseRoutes.Channel.Handle("", ApiSessionRequired(getChannel)).Methods("GET") BaseRoutes.Channel.Handle("", ApiSessionRequired(updateChannel)).Methods("PUT") + BaseRoutes.Channel.Handle("", ApiSessionRequired(deleteChannel)).Methods("DELETE") BaseRoutes.ChannelByName.Handle("", ApiSessionRequired(getChannelByName)).Methods("GET") BaseRoutes.ChannelByNameForTeamName.Handle("", ApiSessionRequired(getChannelByNameForTeamName)).Methods("GET") @@ -226,6 +227,40 @@ func getPublicChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Request } } +func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireChannelId() + if c.Err != nil { + return + } + + var channel *model.Channel + var err *model.AppError + if channel, err = app.GetChannel(c.Params.ChannelId); err != nil { + c.Err = err + return + } + + if channel.Type == model.CHANNEL_OPEN && !app.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_DELETE_PUBLIC_CHANNEL) { + c.SetPermissionError(model.PERMISSION_DELETE_PUBLIC_CHANNEL) + return + } + + if channel.Type == model.CHANNEL_PRIVATE && !app.SessionHasPermissionToChannel(c.Session, channel.Id, model.PERMISSION_DELETE_PRIVATE_CHANNEL) { + c.SetPermissionError(model.PERMISSION_DELETE_PRIVATE_CHANNEL) + return + } + + err = app.DeleteChannel(channel, c.Session.UserId) + if err != nil { + c.Err = err + return + } + + c.LogAudit("name=" + channel.Name) + + ReturnStatusOK(w) +} + func getChannelByName(c *Context, w http.ResponseWriter, r *http.Request) { c.RequireTeamId().RequireChannelName() if c.Err != nil { diff --git a/api4/channel_test.go b/api4/channel_test.go index 27fe5aa44..97364a73e 100644 --- a/api4/channel_test.go +++ b/api4/channel_test.go @@ -11,6 +11,7 @@ import ( "github.com/mattermost/platform/app" "github.com/mattermost/platform/model" + "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" ) @@ -406,6 +407,244 @@ func TestGetPublicChannelsForTeam(t *testing.T) { CheckNoError(t, resp) } +func TestDeleteChannel(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer TearDown() + Client := th.Client + team := th.BasicTeam + user := th.BasicUser + user2 := th.BasicUser2 + + // successful delete of public channel + publicChannel1 := th.CreatePublicChannel() + pass, resp := Client.DeleteChannel(publicChannel1.Id) + CheckNoError(t, resp) + + if !pass { + t.Fatal("should have passed") + } + + if ch, err := app.GetChannel(publicChannel1.Id); err == nil && ch.DeleteAt == 0 { + t.Fatal("should have failed to get deleted channel") + } else if err := app.JoinChannel(ch, user2.Id); err == nil { + t.Fatal("should have failed to join deleted channel") + } + + post1 := &model.Post{ChannelId: publicChannel1.Id, Message: "a" + GenerateTestId() + "a"} + if _, err := Client.CreatePost(post1); err == nil { + t.Fatal("should have failed to post to deleted channel") + } + + // successful delete of private channel + privateChannel2 := th.CreatePrivateChannel() + _, resp = Client.DeleteChannel(privateChannel2.Id) + CheckNoError(t, resp) + + // successful delete of channel with multiple members + publicChannel3 := th.CreatePublicChannel() + app.AddUserToChannel(user2, publicChannel3) + _, resp = Client.DeleteChannel(publicChannel3.Id) + CheckNoError(t, resp) + + // successful delete by TeamAdmin of channel created by user + publicChannel4 := th.CreatePublicChannel() + th.LoginTeamAdmin() + _, resp = Client.DeleteChannel(publicChannel4.Id) + CheckNoError(t, resp) + + // default channel cannot be deleted. + defaultChannel, _ := app.GetChannelByName(model.DEFAULT_CHANNEL, team.Id) + pass, resp = Client.DeleteChannel(defaultChannel.Id) + CheckBadRequestStatus(t, resp) + + if pass { + t.Fatal("should have failed") + } + + th.LoginBasic() + publicChannel5 := th.CreatePublicChannel() + Client.Logout() + + Client.Login(user2.Id, user2.Password) + _, resp = Client.DeleteChannel(publicChannel5.Id) + CheckUnauthorizedStatus(t, resp) + + _, resp = Client.DeleteChannel("junk") + CheckUnauthorizedStatus(t, resp) + + Client.Logout() + _, resp = Client.DeleteChannel(GenerateTestId()) + CheckUnauthorizedStatus(t, resp) + + _, resp = th.SystemAdminClient.DeleteChannel(publicChannel5.Id) + CheckNoError(t, resp) + + isLicensed := utils.IsLicensed + license := utils.License + restrictPublicChannel := *utils.Cfg.TeamSettings.RestrictPublicChannelManagement + restrictPrivateChannel := *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement + defer func() { + *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel + utils.IsLicensed = isLicensed + utils.License = license + utils.SetDefaultRolesBasedOnConfig() + }() + *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL + utils.IsLicensed = true + utils.License = &model.License{Features: &model.Features{}} + utils.License.Features.SetDefaults() + utils.SetDefaultRolesBasedOnConfig() + + th = Setup().InitBasic().InitSystemAdmin() + Client = th.Client + team = th.BasicTeam + user = th.BasicUser + + // channels created by SystemAdmin + publicChannel6 := th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_OPEN) + privateChannel7 := th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) + app.AddUserToChannel(user, publicChannel6) + app.AddUserToChannel(user, privateChannel7) + + // successful delete by user + _, resp = Client.DeleteChannel(publicChannel6.Id) + CheckNoError(t, resp) + + _, resp = Client.DeleteChannel(privateChannel7.Id) + CheckNoError(t, resp) + + *utils.Cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_CHANNEL_ADMIN + *utils.Cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_CHANNEL_ADMIN + utils.SetDefaultRolesBasedOnConfig() + + // channels created by SystemAdmin + publicChannel6 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_OPEN) + privateChannel7 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) + app.AddUserToChannel(user, publicChannel6) + app.AddUserToChannel(user, privateChannel7) + + // cannot delete by user + _, resp = Client.DeleteChannel(publicChannel6.Id) + CheckForbiddenStatus(t, resp) + + _, resp = Client.DeleteChannel(privateChannel7.Id) + CheckForbiddenStatus(t, resp) + + // successful delete by channel admin + MakeUserChannelAdmin(user, publicChannel6) + MakeUserChannelAdmin(user, privateChannel7) + store.ClearChannelCaches() + + _, resp = Client.DeleteChannel(publicChannel6.Id) + CheckNoError(t, resp) + + _, resp = Client.DeleteChannel(privateChannel7.Id) + CheckNoError(t, resp) + + // // channels created by SystemAdmin + publicChannel6 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_OPEN) + privateChannel7 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) + app.AddUserToChannel(user, publicChannel6) + app.AddUserToChannel(user, privateChannel7) + + // successful delete by team admin + UpdateUserToTeamAdmin(user, team) + app.InvalidateAllCaches() + + _, resp = Client.DeleteChannel(publicChannel6.Id) + CheckNoError(t, resp) + + _, resp = Client.DeleteChannel(privateChannel7.Id) + CheckNoError(t, resp) + + *utils.Cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_TEAM_ADMIN + *utils.Cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_TEAM_ADMIN + utils.SetDefaultRolesBasedOnConfig() + UpdateUserToNonTeamAdmin(user, team) + app.InvalidateAllCaches() + + // channels created by SystemAdmin + publicChannel6 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_OPEN) + privateChannel7 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) + app.AddUserToChannel(user, publicChannel6) + app.AddUserToChannel(user, privateChannel7) + + // cannot delete by user + _, resp = Client.DeleteChannel(publicChannel6.Id) + CheckForbiddenStatus(t, resp) + + _, resp = Client.DeleteChannel(privateChannel7.Id) + CheckForbiddenStatus(t, resp) + + // // cannot delete by channel admin + MakeUserChannelAdmin(user, publicChannel6) + MakeUserChannelAdmin(user, privateChannel7) + store.ClearChannelCaches() + + _, resp = Client.DeleteChannel(publicChannel6.Id) + CheckForbiddenStatus(t, resp) + + _, resp = Client.DeleteChannel(privateChannel7.Id) + CheckForbiddenStatus(t, resp) + + // successful delete by team admin + UpdateUserToTeamAdmin(th.BasicUser, team) + app.InvalidateAllCaches() + + _, resp = Client.DeleteChannel(publicChannel6.Id) + CheckNoError(t, resp) + + _, resp = Client.DeleteChannel(privateChannel7.Id) + CheckNoError(t, resp) + + *utils.Cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_SYSTEM_ADMIN + *utils.Cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_SYSTEM_ADMIN + utils.SetDefaultRolesBasedOnConfig() + + // channels created by SystemAdmin + publicChannel6 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_OPEN) + privateChannel7 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) + app.AddUserToChannel(user, publicChannel6) + app.AddUserToChannel(user, privateChannel7) + + // cannot delete by user + _, resp = Client.DeleteChannel(publicChannel6.Id) + CheckForbiddenStatus(t, resp) + + _, resp = Client.DeleteChannel(privateChannel7.Id) + CheckForbiddenStatus(t, resp) + + // cannot delete by channel admin + MakeUserChannelAdmin(user, publicChannel6) + MakeUserChannelAdmin(user, privateChannel7) + store.ClearChannelCaches() + + _, resp = Client.DeleteChannel(publicChannel6.Id) + CheckForbiddenStatus(t, resp) + + _, resp = Client.DeleteChannel(privateChannel7.Id) + CheckForbiddenStatus(t, resp) + + // cannot delete by team admin + UpdateUserToTeamAdmin(th.BasicUser, team) + app.InvalidateAllCaches() + + _, resp = Client.DeleteChannel(publicChannel6.Id) + CheckForbiddenStatus(t, resp) + + _, resp = Client.DeleteChannel(privateChannel7.Id) + CheckForbiddenStatus(t, resp) + + // successful delete by SystemAdmin + _, resp = th.SystemAdminClient.DeleteChannel(publicChannel6.Id) + CheckNoError(t, resp) + + _, resp = th.SystemAdminClient.DeleteChannel(privateChannel7.Id) + CheckNoError(t, resp) +} + func TestGetChannelByName(t *testing.T) { th := Setup().InitBasic().InitSystemAdmin() defer TearDown() diff --git a/app/channel.go b/app/channel.go index 59b71c0a5..49d55e52f 100644 --- a/app/channel.go +++ b/app/channel.go @@ -703,6 +703,9 @@ func GetChannelCounts(teamId string, userId string) (*model.ChannelCounts, *mode } func JoinChannel(channel *model.Channel, userId string) *model.AppError { + if channel.DeleteAt > 0 { + return model.NewLocAppError("JoinChannel", "api.channel.join_channel.already_deleted.app_error", nil, "") + } userChan := Srv.Store.User().Get(userId) memberChan := Srv.Store.Channel().GetMember(channel.Id, userId) diff --git a/i18n/en.json b/i18n/en.json index 017ac7bc0..04a312115 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -303,6 +303,10 @@ "id": "api.channel.join_channel.permissions.app_error", "translation": "You do not have the appropriate permissions" }, + { + "id": "api.channel.join_channel.already_deleted.app_error", + "translation": "Channel is already deleted" + }, { "id": "api.channel.join_channel.post_and_forget", "translation": "%v has joined the channel." diff --git a/model/client4.go b/model/client4.go index 16e8bf7d3..38b89e74f 100644 --- a/model/client4.go +++ b/model/client4.go @@ -841,6 +841,16 @@ func (c *Client4) GetPublicChannelsForTeam(teamId string, page int, perPage int, } } +// DeleteChannel deletes channel based on the provided channel id string. +func (c *Client4) DeleteChannel(channelId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetChannelRoute(channelId)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + // GetChannelByName returns a channel based on the provided channel name and team id strings. func (c *Client4) GetChannelByName(channelName, teamId string, etag string) (*Channel, *Response) { if r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId), etag); err != nil { -- cgit v1.2.3-1-g7c22