diff --git a/api/channel.go b/api/channel.go index b4a5b78dc3..038a4286af 100644 --- a/api/channel.go +++ b/api/channel.go @@ -59,6 +59,11 @@ func createChannel(c *Context, w http.ResponseWriter, r *http.Request) { channel.TeamId = c.TeamId } + if err := CanManageChannel(c, channel); err != nil { + c.Err = err + return + } + if !c.HasPermissionsToTeam(channel.TeamId, "createChannel") { return } @@ -178,6 +183,32 @@ func CreateDefaultChannels(c *Context, teamId string) ([]*model.Channel, *model. return channels, nil } +func CanManageChannel(c *Context, channel *model.Channel) *model.AppError { + if utils.IsLicensed { + if channel.Type == model.CHANNEL_OPEN { + if *utils.Cfg.TeamSettings.RestrictPublicChannelManagement == model.PERMISSIONS_SYSTEM_ADMIN && !c.IsSystemAdmin() { + return model.NewLocAppError("CanManageChannel", "api.channel.can_manage_channel.public_restricted_system_admin.app_error", nil, "") + } + + if *utils.Cfg.TeamSettings.RestrictPublicChannelManagement == model.PERMISSIONS_TEAM_ADMIN && !c.IsTeamAdmin() { + return model.NewLocAppError("CanManageChannel", "api.channel.can_manage_channel.public_restricted_team_admin.app_error", nil, "") + } + } + + if channel.Type == model.CHANNEL_PRIVATE { + if *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement == model.PERMISSIONS_SYSTEM_ADMIN && !c.IsSystemAdmin() { + return model.NewLocAppError("CanManageChannel", "api.channel.can_manage_channel.private_restricted_system_admin.app_error", nil, "") + } + + if *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement == model.PERMISSIONS_TEAM_ADMIN && !c.IsTeamAdmin() { + return model.NewLocAppError("CanManageChannel", "api.channel.can_manage_channel.private_restricted_team_admin.app_error", nil, "") + } + } + } + + return nil +} + func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) { channel := model.ChannelFromJson(r.Body) @@ -198,15 +229,14 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } else { oldChannel := cresult.Data.(*model.Channel) - channelMember := cmcresult.Data.(model.ChannelMember) + // Don't need to do anything with channel member, just wanted to confirm it exists - if !c.HasPermissionsToTeam(oldChannel.TeamId, "updateChannel") { + if err := CanManageChannel(c, oldChannel); err != nil { + c.Err = err return } - if !strings.Contains(channelMember.Roles, model.CHANNEL_ROLE_ADMIN) && !c.IsTeamAdmin() { - c.Err = model.NewLocAppError("updateChannel", "api.channel.update_channel.permission.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden + if !c.HasPermissionsToTeam(oldChannel.TeamId, "updateChannel") { return } @@ -275,7 +305,12 @@ func updateChannelHeader(c *Context, w http.ResponseWriter, r *http.Request) { return } else { channel := cresult.Data.(*model.Channel) - // Don't need to do anything channel member, just wanted to confirm it exists + // Don't need to do anything with channel member, just wanted to confirm it exists + + if err := CanManageChannel(c, channel); err != nil { + c.Err = err + return + } if channel.TeamId != "" && !c.HasPermissionsToTeam(channel.TeamId, "updateChannelHeader") { return @@ -348,7 +383,12 @@ func updateChannelPurpose(c *Context, w http.ResponseWriter, r *http.Request) { return } else { channel := cresult.Data.(*model.Channel) - // Don't need to do anything channel member, just wanted to confirm it exists + // Don't need to do anything with channel member, just wanted to confirm it exists + + if err := CanManageChannel(c, channel); err != nil { + c.Err = err + return + } if !c.HasPermissionsToTeam(channel.TeamId, "updateChannelPurpose") { return @@ -646,6 +686,7 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { sc := Srv.Store.Channel().Get(id) scm := Srv.Store.Channel().GetMember(id, c.Session.UserId) + cmc := Srv.Store.Channel().GetMemberCount(id) uc := Srv.Store.User().Get(c.Session.UserId) ihc := Srv.Store.Webhook().GetIncomingByChannel(id) ohc := Srv.Store.Webhook().GetOutgoingByChannel(id) @@ -659,6 +700,9 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { } else if scmresult := <-scm; scmresult.Err != nil { c.Err = scmresult.Err return + } else if cmcresult := <-cmc; cmcresult.Err != nil { + c.Err = cmcresult.Err + return } else if ihcresult := <-ihc; ihcresult.Err != nil { c.Err = ihcresult.Err return @@ -667,18 +711,21 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } else { channel := cresult.Data.(*model.Channel) + memberCount := cmcresult.Data.(int64) user := uresult.Data.(*model.User) - channelMember := scmresult.Data.(model.ChannelMember) incomingHooks := ihcresult.Data.([]*model.IncomingWebhook) outgoingHooks := ohcresult.Data.([]*model.OutgoingWebhook) + // Don't need to do anything with channel member, just wanted to confirm it exists - if !c.HasPermissionsToTeam(channel.TeamId, "deleteChannel") { - return + // Allow delete if user is the only member left in channel + if memberCount > 1 { + if err := CanManageChannel(c, channel); err != nil { + c.Err = err + return + } } - if !strings.Contains(channelMember.Roles, model.CHANNEL_ROLE_ADMIN) && !c.IsTeamAdmin() { - c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.permissions.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden + if !c.HasPermissionsToTeam(channel.TeamId, "deleteChannel") { return } diff --git a/api/channel_test.go b/api/channel_test.go index 7480dea233..ac47d4eeda 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -14,8 +14,9 @@ import ( ) func TestCreateChannel(t *testing.T) { - th := Setup().InitBasic() + th := Setup().InitBasic().InitSystemAdmin() Client := th.BasicClient + SystemAdminClient := th.SystemAdminClient team := th.BasicTeam Client.Must(Client.Logout()) team2 := th.CreateTeam(th.BasicClient) @@ -74,6 +75,12 @@ func TestCreateChannel(t *testing.T) { t.Fatal(err) } + channel = model.Channel{DisplayName: "Channel With No TeamId", Name: "aaaa" + model.NewId() + "abbb", Type: model.CHANNEL_OPEN, TeamId: ""} + + if _, err := Client.CreateChannel(&channel); err != nil { + t.Fatal(err) + } + channel = model.Channel{DisplayName: "Test API Name", Name: model.NewId() + "__" + model.NewId(), Type: model.CHANNEL_OPEN, TeamId: team.Id} if _, err := Client.CreateChannel(&channel); err == nil { @@ -85,6 +92,72 @@ func TestCreateChannel(t *testing.T) { if _, err := Client.CreateChannel(&channel); err == nil { t.Fatal("Should have errored out on direct channel type") } + + isLicensed := utils.IsLicensed + 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.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL + utils.IsLicensed = true + + channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} + channel3 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} + if _, err := Client.CreateChannel(channel2); err != nil { + t.Fatal(err) + } + if _, err := Client.CreateChannel(channel3); err != nil { + t.Fatal(err) + } + + *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN + + channel2.Name = "a" + model.NewId() + "a" + channel3.Name = "a" + model.NewId() + "a" + if _, err := Client.CreateChannel(channel2); err == nil { + t.Fatal("should have errored not team admin") + } + if _, err := Client.CreateChannel(channel3); err == nil { + t.Fatal("should have errored not team admin") + } + + UpdateUserToTeamAdmin(th.BasicUser, team) + Client.Logout() + th.LoginBasic() + Client.SetTeamId(team.Id) + + if _, err := Client.CreateChannel(channel2); err != nil { + t.Fatal(err) + } + if _, err := Client.CreateChannel(channel3); err != nil { + t.Fatal(err) + } + + *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + + channel2.Name = "a" + model.NewId() + "a" + channel3.Name = "a" + model.NewId() + "a" + if _, err := Client.CreateChannel(channel2); err == nil { + t.Fatal("should have errored not system admin") + } + if _, err := Client.CreateChannel(channel3); err == nil { + t.Fatal("should have errored not system admin") + } + + LinkUserToTeam(th.SystemAdminUser, team) + + if _, err := SystemAdminClient.CreateChannel(channel2); err != nil { + t.Fatal(err) + } + if _, err := SystemAdminClient.CreateChannel(channel3); err != nil { + t.Fatal(err) + } } func TestCreateDirectChannel(t *testing.T) { @@ -129,7 +202,7 @@ func TestCreateDirectChannel(t *testing.T) { } func TestUpdateChannel(t *testing.T) { - th := Setup().InitSystemAdmin() + th := Setup().InitBasic().InitSystemAdmin() Client := th.SystemAdminClient team := th.SystemAdminTeam sysAdminUser := th.SystemAdminUser @@ -193,6 +266,7 @@ func TestUpdateChannel(t *testing.T) { } Client.Login(sysAdminUser.Email, sysAdminUser.Password) + LinkUserToTeam(sysAdminUser, team) Client.Must(Client.JoinChannel(channel1.Id)) if _, err := Client.UpdateChannel(upChannel1); err != nil { @@ -204,11 +278,82 @@ func TestUpdateChannel(t *testing.T) { if _, err := Client.UpdateChannel(upChannel1); err == nil { t.Fatal("should have failed - channel deleted") } + + isLicensed := utils.IsLicensed + 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.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL + utils.IsLicensed = true + + channel2 := th.CreateChannel(Client, team) + channel3 := th.CreatePrivateChannel(Client, team) + + LinkUserToTeam(th.BasicUser, team) + + Client.Must(Client.AddChannelMember(channel2.Id, th.BasicUser.Id)) + Client.Must(Client.AddChannelMember(channel3.Id, th.BasicUser.Id)) + + Client.Login(th.BasicUser.Email, th.BasicUser.Password) + + if _, err := Client.UpdateChannel(channel2); err != nil { + t.Fatal(err) + } + if _, err := Client.UpdateChannel(channel3); err != nil { + t.Fatal(err) + } + + *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN + + if _, err := Client.UpdateChannel(channel2); err == nil { + t.Fatal("should have errored not team admin") + } + if _, err := Client.UpdateChannel(channel3); err == nil { + t.Fatal("should have errored not team admin") + } + + UpdateUserToTeamAdmin(th.BasicUser, team) + Client.Logout() + Client.Login(th.BasicUser.Email, th.BasicUser.Password) + Client.SetTeamId(team.Id) + + if _, err := Client.UpdateChannel(channel2); err != nil { + t.Fatal(err) + } + if _, err := Client.UpdateChannel(channel3); err != nil { + t.Fatal(err) + } + + *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + + if _, err := Client.UpdateChannel(channel2); err == nil { + t.Fatal("should have errored not system admin") + } + if _, err := Client.UpdateChannel(channel3); err == nil { + t.Fatal("should have errored not system admin") + } + + th.LoginSystemAdmin() + + if _, err := Client.UpdateChannel(channel2); err != nil { + t.Fatal(err) + } + if _, err := Client.UpdateChannel(channel3); err != nil { + t.Fatal(err) + } } func TestUpdateChannelHeader(t *testing.T) { - th := Setup().InitBasic() + th := Setup().InitBasic().InitSystemAdmin() Client := th.BasicClient + SystemAdminClient := th.SystemAdminClient team := th.BasicTeam channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} @@ -266,11 +411,89 @@ func TestUpdateChannelHeader(t *testing.T) { if _, err := Client.UpdateChannelHeader(data); err == nil { t.Fatal("should have errored non-channel member trying to update header") } + + isLicensed := utils.IsLicensed + 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.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL + utils.IsLicensed = true + + th.LoginBasic() + channel2 := th.CreateChannel(Client, team) + channel3 := th.CreatePrivateChannel(Client, team) + + data2 := make(map[string]string) + data2["channel_id"] = channel2.Id + data2["channel_header"] = "new header" + + data3 := make(map[string]string) + data3["channel_id"] = channel3.Id + data3["channel_header"] = "new header" + + Client.Login(th.BasicUser.Email, th.BasicUser.Password) + + if _, err := Client.UpdateChannelHeader(data2); err != nil { + t.Fatal(err) + } + if _, err := Client.UpdateChannelHeader(data3); err != nil { + t.Fatal(err) + } + + *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN + + if _, err := Client.UpdateChannelHeader(data2); err == nil { + t.Fatal("should have errored not team admin") + } + if _, err := Client.UpdateChannelHeader(data3); err == nil { + t.Fatal("should have errored not team admin") + } + + UpdateUserToTeamAdmin(th.BasicUser, team) + Client.Logout() + th.LoginBasic() + Client.SetTeamId(team.Id) + + if _, err := Client.UpdateChannelHeader(data2); err != nil { + t.Fatal(err) + } + if _, err := Client.UpdateChannelHeader(data3); err != nil { + t.Fatal(err) + } + + *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + + if _, err := Client.UpdateChannelHeader(data2); err == nil { + t.Fatal("should have errored not system admin") + } + if _, err := Client.UpdateChannelHeader(data3); err == nil { + t.Fatal("should have errored not system admin") + } + + LinkUserToTeam(th.SystemAdminUser, team) + Client.Must(Client.AddChannelMember(channel2.Id, th.SystemAdminUser.Id)) + Client.Must(Client.AddChannelMember(channel3.Id, th.SystemAdminUser.Id)) + th.LoginSystemAdmin() + + if _, err := SystemAdminClient.UpdateChannelHeader(data2); err != nil { + t.Fatal(err) + } + if _, err := SystemAdminClient.UpdateChannelHeader(data3); err != nil { + t.Fatal(err) + } } func TestUpdateChannelPurpose(t *testing.T) { - th := Setup().InitBasic() + th := Setup().InitBasic().InitSystemAdmin() Client := th.BasicClient + SystemAdminClient := th.SystemAdminClient team := th.BasicTeam channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} @@ -314,6 +537,83 @@ func TestUpdateChannelPurpose(t *testing.T) { if _, err := Client.UpdateChannelPurpose(data); err == nil { t.Fatal("should have errored non-channel member trying to update purpose") } + + isLicensed := utils.IsLicensed + 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.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL + utils.IsLicensed = true + + th.LoginBasic() + channel2 := th.CreateChannel(Client, team) + channel3 := th.CreatePrivateChannel(Client, team) + + data2 := make(map[string]string) + data2["channel_id"] = channel2.Id + data2["channel_purpose"] = "new purpose" + + data3 := make(map[string]string) + data3["channel_id"] = channel3.Id + data3["channel_purpose"] = "new purpose" + + Client.Login(th.BasicUser.Email, th.BasicUser.Password) + + if _, err := Client.UpdateChannelPurpose(data2); err != nil { + t.Fatal(err) + } + if _, err := Client.UpdateChannelPurpose(data3); err != nil { + t.Fatal(err) + } + + *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN + + if _, err := Client.UpdateChannelPurpose(data2); err == nil { + t.Fatal("should have errored not team admin") + } + if _, err := Client.UpdateChannelPurpose(data3); err == nil { + t.Fatal("should have errored not team admin") + } + + UpdateUserToTeamAdmin(th.BasicUser, team) + Client.Logout() + th.LoginBasic() + Client.SetTeamId(team.Id) + + if _, err := Client.UpdateChannelPurpose(data2); err != nil { + t.Fatal(err) + } + if _, err := Client.UpdateChannelPurpose(data3); err != nil { + t.Fatal(err) + } + + *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + + if _, err := Client.UpdateChannelPurpose(data2); err == nil { + t.Fatal("should have errored not system admin") + } + if _, err := Client.UpdateChannelPurpose(data3); err == nil { + t.Fatal("should have errored not system admin") + } + + LinkUserToTeam(th.SystemAdminUser, team) + Client.Must(Client.AddChannelMember(channel2.Id, th.SystemAdminUser.Id)) + Client.Must(Client.AddChannelMember(channel3.Id, th.SystemAdminUser.Id)) + th.LoginSystemAdmin() + + if _, err := SystemAdminClient.UpdateChannelPurpose(data2); err != nil { + t.Fatal(err) + } + if _, err := SystemAdminClient.UpdateChannelPurpose(data3); err != nil { + t.Fatal(err) + } } func TestGetChannel(t *testing.T) { @@ -572,7 +872,7 @@ func TestLeaveChannel(t *testing.T) { } func TestDeleteChannel(t *testing.T) { - th := Setup().InitSystemAdmin() + th := Setup().InitBasic().InitSystemAdmin() Client := th.SystemAdminClient team := th.SystemAdminTeam userSystemAdmin := th.SystemAdminUser @@ -619,8 +919,8 @@ func TestDeleteChannel(t *testing.T) { Client.Must(Client.JoinChannel(channel2.Id)) - if _, err := Client.DeleteChannel(channel2.Id); err == nil { - t.Fatal("should have failed to delete channel you're not an admin of") + if _, err := Client.DeleteChannel(channel2.Id); err != nil { + t.Fatal(err) } rget := Client.Must(Client.GetChannels("")) @@ -640,6 +940,8 @@ func TestDeleteChannel(t *testing.T) { Client.Login(userStd.Email, userStd.Password) Client.SetTeamId(team.Id) + channel2 = th.CreateChannel(Client, team) + if _, err := Client.DeleteChannel(channel2.Id); err != nil { t.Fatal(err) } @@ -657,6 +959,102 @@ func TestDeleteChannel(t *testing.T) { if _, err := Client.DeleteChannel(channel3.Id); err == nil { t.Fatal("should have failed - channel already deleted") } + + isLicensed := utils.IsLicensed + 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.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL + utils.IsLicensed = true + + th.LoginSystemAdmin() + LinkUserToTeam(th.BasicUser, team) + + channel2 = th.CreateChannel(Client, team) + channel3 = th.CreatePrivateChannel(Client, team) + channel4 := th.CreateChannel(Client, team) + Client.Must(Client.AddChannelMember(channel2.Id, th.BasicUser.Id)) + Client.Must(Client.AddChannelMember(channel3.Id, th.BasicUser.Id)) + Client.Must(Client.AddChannelMember(channel4.Id, th.BasicUser.Id)) + Client.Must(Client.LeaveChannel(channel4.Id)) + + Client.Login(th.BasicUser.Email, th.BasicUser.Password) + + if _, err := Client.DeleteChannel(channel2.Id); err != nil { + t.Fatal(err) + } + if _, err := Client.DeleteChannel(channel3.Id); err != nil { + t.Fatal(err) + } + + *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN + + th.LoginSystemAdmin() + + channel2 = th.CreateChannel(Client, team) + channel3 = th.CreatePrivateChannel(Client, team) + Client.Must(Client.AddChannelMember(channel2.Id, th.BasicUser.Id)) + Client.Must(Client.AddChannelMember(channel3.Id, th.BasicUser.Id)) + + Client.Login(th.BasicUser.Email, th.BasicUser.Password) + + if _, err := Client.DeleteChannel(channel2.Id); err == nil { + t.Fatal("should have errored not team admin") + } + if _, err := Client.DeleteChannel(channel3.Id); err == nil { + t.Fatal("should have errored not team admin") + } + + UpdateUserToTeamAdmin(th.BasicUser, team) + Client.Logout() + Client.Login(th.BasicUser.Email, th.BasicUser.Password) + Client.SetTeamId(team.Id) + + if _, err := Client.DeleteChannel(channel2.Id); err != nil { + t.Fatal(err) + } + if _, err := Client.DeleteChannel(channel3.Id); err != nil { + t.Fatal(err) + } + + *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + + th.LoginSystemAdmin() + + channel2 = th.CreateChannel(Client, team) + channel3 = th.CreatePrivateChannel(Client, team) + Client.Must(Client.AddChannelMember(channel2.Id, th.BasicUser.Id)) + Client.Must(Client.AddChannelMember(channel3.Id, th.BasicUser.Id)) + + Client.Login(th.BasicUser.Email, th.BasicUser.Password) + + if _, err := Client.DeleteChannel(channel2.Id); err == nil { + t.Fatal("should have errored not system admin") + } + if _, err := Client.DeleteChannel(channel3.Id); err == nil { + t.Fatal("should have errored not system admin") + } + + // Only one left in channel, should be able to delete + if _, err := Client.DeleteChannel(channel4.Id); err != nil { + t.Fatal(err) + } + + th.LoginSystemAdmin() + + if _, err := Client.DeleteChannel(channel2.Id); err != nil { + t.Fatal(err) + } + if _, err := Client.DeleteChannel(channel3.Id); err != nil { + t.Fatal(err) + } } func TestGetChannelExtraInfo(t *testing.T) { diff --git a/api/team.go b/api/team.go index 9b23a63afc..50e32e6259 100644 --- a/api/team.go +++ b/api/team.go @@ -400,12 +400,12 @@ func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) { } if utils.IsLicensed { - if *utils.Cfg.TeamSettings.RestrictTeamInvite == model.TEAM_INVITE_SYSTEM_ADMIN && !c.IsSystemAdmin() { + if *utils.Cfg.TeamSettings.RestrictTeamInvite == model.PERMISSIONS_SYSTEM_ADMIN && !c.IsSystemAdmin() { c.Err = model.NewLocAppError("inviteMembers", "api.team.invite_members.restricted_system_admin.app_error", nil, "") return } - if *utils.Cfg.TeamSettings.RestrictTeamInvite == model.TEAM_INVITE_TEAM_ADMIN && !c.IsTeamAdmin() { + if *utils.Cfg.TeamSettings.RestrictTeamInvite == model.PERMISSIONS_TEAM_ADMIN && !c.IsTeamAdmin() { c.Err = model.NewLocAppError("inviteMembers", "api.team.invite_members.restricted_team_admin.app_error", nil, "") return } diff --git a/api/team_test.go b/api/team_test.go index 91c73bed54..a62ffcdb58 100644 --- a/api/team_test.go +++ b/api/team_test.go @@ -399,7 +399,7 @@ func TestInviteMembers(t *testing.T) { defer func() { *utils.Cfg.TeamSettings.RestrictTeamInvite = restrictTeamInvite }() - *utils.Cfg.TeamSettings.RestrictTeamInvite = model.TEAM_INVITE_TEAM_ADMIN + *utils.Cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN th.LoginBasic2() LinkUserToTeam(th.BasicUser2, team) @@ -427,7 +427,7 @@ func TestInviteMembers(t *testing.T) { t.Fatal(err) } - *utils.Cfg.TeamSettings.RestrictTeamInvite = model.TEAM_INVITE_SYSTEM_ADMIN + *utils.Cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_SYSTEM_ADMIN if _, err := Client.InviteMembers(invites); err == nil { t.Fatal("should have errored not system admin and licensed") diff --git a/config/config.json b/config/config.json index ec021045f7..c70ace9ff0 100644 --- a/config/config.json +++ b/config/config.json @@ -38,7 +38,9 @@ "EnableCustomBrand": false, "CustomBrandText": "", "RestrictDirectMessage": "any", - "RestrictTeamInvite": "system_admin" + "RestrictTeamInvite": "all", + "RestrictPublicChannelManagement": "all", + "RestrictPrivateChannelManagement": "all" }, "SqlSettings": { "DriverName": "mysql", diff --git a/i18n/en.json b/i18n/en.json index 69b18adf37..8436d48d9e 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -311,6 +311,22 @@ "id": "api.channel.update_channel.deleted.app_error", "translation": "The channel has been archived or deleted" }, + { + "id": "api.channel.can_manage_channel.public_restricted_system_admin.app_error", + "translation": "Public Channel management and creation is restricted to System Administrators." + }, + { + "id": "api.channel.can_manage_channel.public_restricted_team_admin.app_error", + "translation": "Public Channel management and creation is restricted to Team and System Administrators." + }, + { + "id": "api.channel.can_manage_channel.private_restricted_system_admin.app_error", + "translation": "Private Group management and creation is restricted to System Administrators." + }, + { + "id": "api.channel.can_manage_channel.private_restricted_team_admin.app_error", + "translation": "Private Group management and creation is restricted to Team and System Administrators." + }, { "id": "api.channel.update_channel.permission.app_error", "translation": "You do not have the appropriate permissions" diff --git a/model/config.go b/model/config.go index 32994a279e..61c39bc5bd 100644 --- a/model/config.go +++ b/model/config.go @@ -32,9 +32,9 @@ const ( DIRECT_MESSAGE_ANY = "any" DIRECT_MESSAGE_TEAM = "team" - TEAM_INVITE_ALL = "all" - TEAM_INVITE_TEAM_ADMIN = "team_admin" - TEAM_INVITE_SYSTEM_ADMIN = "system_admin" + PERMISSIONS_ALL = "all" + PERMISSIONS_TEAM_ADMIN = "team_admin" + PERMISSIONS_SYSTEM_ADMIN = "system_admin" FAKE_SETTING = "********************************" @@ -169,17 +169,19 @@ type SupportSettings struct { } type TeamSettings struct { - SiteName string - MaxUsersPerTeam int - EnableTeamCreation bool - EnableUserCreation bool - EnableOpenServer *bool - RestrictCreationToDomains string - RestrictTeamNames *bool - EnableCustomBrand *bool - CustomBrandText *string - RestrictDirectMessage *string - RestrictTeamInvite *string + SiteName string + MaxUsersPerTeam int + EnableTeamCreation bool + EnableUserCreation bool + EnableOpenServer *bool + RestrictCreationToDomains string + RestrictTeamNames *bool + EnableCustomBrand *bool + CustomBrandText *string + RestrictDirectMessage *string + RestrictTeamInvite *string + RestrictPublicChannelManagement *string + RestrictPrivateChannelManagement *string } type LdapSettings struct { @@ -381,7 +383,17 @@ func (o *Config) SetDefaults() { if o.TeamSettings.RestrictTeamInvite == nil { o.TeamSettings.RestrictTeamInvite = new(string) - *o.TeamSettings.RestrictTeamInvite = TEAM_INVITE_ALL + *o.TeamSettings.RestrictTeamInvite = PERMISSIONS_ALL + } + + if o.TeamSettings.RestrictPublicChannelManagement == nil { + o.TeamSettings.RestrictPublicChannelManagement = new(string) + *o.TeamSettings.RestrictPublicChannelManagement = PERMISSIONS_ALL + } + + if o.TeamSettings.RestrictPrivateChannelManagement == nil { + o.TeamSettings.RestrictPrivateChannelManagement = new(string) + *o.TeamSettings.RestrictPrivateChannelManagement = PERMISSIONS_ALL } if o.EmailSettings.EnableSignInWithEmail == nil { diff --git a/utils/config.go b/utils/config.go index abb24c0850..1fbed5d7bc 100644 --- a/utils/config.go +++ b/utils/config.go @@ -215,6 +215,8 @@ func getClientConfig(c *model.Config) map[string]string { props["RestrictTeamNames"] = strconv.FormatBool(*c.TeamSettings.RestrictTeamNames) props["RestrictDirectMessage"] = *c.TeamSettings.RestrictDirectMessage props["RestrictTeamInvite"] = *c.TeamSettings.RestrictTeamInvite + props["RestrictPublicChannelManagement"] = *c.TeamSettings.RestrictPublicChannelManagement + props["RestrictPrivateChannelManagement"] = *c.TeamSettings.RestrictPrivateChannelManagement props["EnableOAuthServiceProvider"] = strconv.FormatBool(c.ServiceSettings.EnableOAuthServiceProvider) props["SegmentDeveloperKey"] = c.ServiceSettings.SegmentDeveloperKey diff --git a/webapp/components/admin_console/policy_settings.jsx b/webapp/components/admin_console/policy_settings.jsx index 7fe8e94605..c7031af7bb 100644 --- a/webapp/components/admin_console/policy_settings.jsx +++ b/webapp/components/admin_console/policy_settings.jsx @@ -21,12 +21,16 @@ export default class PolicySettings extends AdminSettings { this.renderSettings = this.renderSettings.bind(this); this.state = Object.assign(this.state, { - restrictTeamInvite: props.config.TeamSettings.RestrictTeamInvite + restrictTeamInvite: props.config.TeamSettings.RestrictTeamInvite, + restrictPublicChannelManagement: props.config.TeamSettings.RestrictPublicChannelManagement, + restrictPrivateChannelManagement: props.config.TeamSettings.RestrictPrivateChannelManagement }); } getConfigFromState(config) { config.TeamSettings.RestrictTeamInvite = this.state.restrictTeamInvite; + config.TeamSettings.RestrictPublicChannelManagement = this.state.restrictPublicChannelManagement; + config.TeamSettings.RestrictPrivateChannelManagement = this.state.restrictPrivateChannelManagement; return config; } @@ -48,9 +52,9 @@ export default class PolicySettings extends AdminSettings { } /> + + } + value={this.state.restrictPublicChannelManagement} + onChange={this.handleChange} + helpText={ + + } + /> + + } + value={this.state.restrictPrivateChannelManagement} + onChange={this.handleChange} + helpText={ + + } + /> ); } diff --git a/webapp/components/channel_header.jsx b/webapp/components/channel_header.jsx index 3449a0fd60..2b9b1e1ccb 100644 --- a/webapp/components/channel_header.jsx +++ b/webapp/components/channel_header.jsx @@ -56,6 +56,7 @@ export default class ChannelHeader extends React.Component { state.showRenameChannelModal = false; this.state = state; } + getStateFromStores() { const extraInfo = ChannelStore.getExtraInfo(this.props.channelId); @@ -67,6 +68,7 @@ export default class ChannelHeader extends React.Component { currentUser: UserStore.getCurrentUser() }; } + validState() { if (!this.state.channel || !this.state.memberChannel || @@ -77,6 +79,7 @@ export default class ChannelHeader extends React.Component { } return true; } + componentDidMount() { ChannelStore.addChangeListener(this.onListenerChange); ChannelStore.addExtraInfoChangeListener(this.onListenerChange); @@ -87,6 +90,7 @@ export default class ChannelHeader extends React.Component { $('.sidebar--left .dropdown-menu').perfectScrollbar(); document.addEventListener('keydown', this.openRecentMentions); } + componentWillUnmount() { ChannelStore.removeChangeListener(this.onListenerChange); ChannelStore.removeExtraInfoChangeListener(this.onListenerChange); @@ -96,6 +100,7 @@ export default class ChannelHeader extends React.Component { UserStore.removeStatusesChangeListener(this.onListenerChange); document.removeEventListener('keydown', this.openRecentMentions); } + onListenerChange() { const newState = this.getStateFromStores(); if (!Utils.areObjectsEqual(newState, this.state)) { @@ -103,6 +108,7 @@ export default class ChannelHeader extends React.Component { } $('.channel-header__info .description').popover({placement: 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}}); } + handleLeave() { Client.leaveChannel(this.state.channel.id, () => { @@ -119,6 +125,7 @@ export default class ChannelHeader extends React.Component { } ); } + searchMentions(e) { e.preventDefault(); @@ -146,12 +153,14 @@ export default class ChannelHeader extends React.Component { is_mention_search: true }); } + openRecentMentions(e) { if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.keyCode === Constants.KeyCodes.M) { e.preventDefault(); this.searchMentions(e); } } + showRenameChannelModal(e) { e.preventDefault(); @@ -159,6 +168,7 @@ export default class ChannelHeader extends React.Component { showRenameChannelModal: true }); } + hideRenameChannelModal() { this.setState({ showRenameChannelModal: false @@ -179,6 +189,30 @@ export default class ChannelHeader extends React.Component { return null; } + showManagementOptions(channel, isAdmin, isSystemAdmin) { + if (global.window.mm_license.IsLicensed !== 'true') { + return true; + } + + if (channel.type === Constants.OPEN_CHANNEL) { + if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) { + return false; + } + if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) { + return false; + } + } else if (channel.type === Constants.PRIVATE_CHANNEL) { + if (global.window.mm_config.RestrictPrivateChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) { + return false; + } + if (global.window.mm_config.RestrictPrivateChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) { + return false; + } + } + + return true; + } + render() { if (!this.validState()) { return null; @@ -210,7 +244,8 @@ export default class ChannelHeader extends React.Component { ); let channelTitle = channel.display_name; const currentId = this.state.currentUser.id; - const isAdmin = Utils.isAdmin(this.state.memberChannel.roles) || TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser(); + const isAdmin = TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser(); + const isSystemAdmin = UserStore.isSystemAdminForCurrentUser(); const isDirect = (this.state.channel.type === 'D'); if (isDirect) { @@ -329,46 +364,6 @@ export default class ChannelHeader extends React.Component { } } - dropdownContents.push( -
  • - - - -
  • - ); - dropdownContents.push( -
  • - this.setState({showEditChannelPurposeModal: true})} - > - - -
  • - ); dropdownContents.push(
  • ); - if (isAdmin) { + const deleteOption = ( +
  • + + + +
  • + ); + + if (this.showManagementOptions(channel, isAdmin, isSystemAdmin)) { + dropdownContents.push( +
  • + + + +
  • + ); + + dropdownContents.push( +
  • + this.setState({showEditChannelPurposeModal: true})} + > + + +
  • + ); + dropdownContents.push(
  • - - - -
  • - ); + dropdownContents.push(deleteOption); } + } else if (this.state.userCount === 1) { + dropdownContents.push(deleteOption); } const canLeave = channel.type === Constants.PRIVATE_CHANNEL ? this.state.userCount > 1 : true; diff --git a/webapp/components/more_channels.jsx b/webapp/components/more_channels.jsx index 54a06d0ae6..b7ffff7121 100644 --- a/webapp/components/more_channels.jsx +++ b/webapp/components/more_channels.jsx @@ -6,8 +6,11 @@ import LoadingScreen from './loading_screen.jsx'; import NewChannelFlow from './new_channel_flow.jsx'; import ChannelStore from 'stores/channel_store.jsx'; +import UserStore from 'stores/user_store.jsx'; +import TeamStore from 'stores/team_store.jsx'; import * as Utils from 'utils/utils.jsx'; +import Constants from 'utils/constants.jsx'; import * as AsyncClient from 'utils/async_client.jsx'; import * as GlobalActions from 'actions/global_actions.jsx'; @@ -132,6 +135,41 @@ export default class MoreChannels extends React.Component { serverError =
    ; } + let createNewChannelButton = ( + + ); + + let createChannelHelpText = ( +

    + +

    + ); + + const isAdmin = TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser(); + const isSystemAdmin = UserStore.isSystemAdminForCurrentUser(); + + if (global.window.mm_license.IsLicensed === 'true') { + if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) { + createNewChannelButton = null; + createChannelHelpText = null; + } else if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) { + createNewChannelButton = null; + createChannelHelpText = null; + } + } + var moreChannels; if (this.state.channels != null) { @@ -153,12 +191,7 @@ export default class MoreChannels extends React.Component { defaultMessage='No more channels to join' />

    -

    - -

    + {createChannelHelpText} ); } @@ -195,16 +228,7 @@ export default class MoreChannels extends React.Component { defaultMessage='More Channels' /> - + {createNewChannelButton}

    {this.props.serverError}

    ; } + let createPublicChannelLink = ( + + + + ); + + let createPrivateChannelLink = ( + + + + ); + + const isAdmin = TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser(); + const isSystemAdmin = UserStore.isSystemAdminForCurrentUser(); + + if (global.window.mm_license.IsLicensed === 'true') { + if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) { + createPublicChannelLink = null; + } else if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) { + createPublicChannelLink = null; + } + + if (global.window.mm_config.RestrictPrivateChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) { + createPrivateChannelLink = null; + } else if (global.window.mm_config.RestrictPrivateChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) { + createPrivateChannelLink = null; + } + } + var channelTerm = ''; var channelSwitchText = ''; switch (this.props.channelType) { @@ -129,15 +174,7 @@ class NewChannelModal extends React.Component { id='channel_modal.privateGroup1' defaultMessage='Create a new private group with restricted membership. ' /> - - - + {createPublicChannelLink} ); break; @@ -154,15 +191,7 @@ class NewChannelModal extends React.Component { id='channel_modal.publicChannel2' defaultMessage='Create a new public channel anyone can join. ' /> - - - + {createPrivateChannelLink} ); break; diff --git a/webapp/components/sidebar.jsx b/webapp/components/sidebar.jsx index 4f678274d3..fdcae1dff1 100644 --- a/webapp/components/sidebar.jsx +++ b/webapp/components/sidebar.jsx @@ -682,6 +682,55 @@ export default class Sidebar extends React.Component { /> ); + const isAdmin = TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser(); + const isSystemAdmin = UserStore.isSystemAdminForCurrentUser(); + + let createPublicChannelIcon = ( + + + {'+'} + + + ); + + let createPrivateChannelIcon = ( + + + {'+'} + + + ); + + if (global.window.mm_license.IsLicensed === 'true') { + if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) { + createPublicChannelIcon = null; + } else if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) { + createPublicChannelIcon = null; + } + + if (global.window.mm_config.RestrictPrivateChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) { + createPrivateChannelIcon = null; + } else if (global.window.mm_config.RestrictPrivateChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) { + createPrivateChannelIcon = null; + } + } + return (
    - - - {'+'} - - + {createPublicChannelIcon} {publicChannelItems} @@ -765,19 +802,7 @@ export default class Sidebar extends React.Component { id='sidebar.pg' defaultMessage='Private Groups' /> - - - {'+'} - - + {createPrivateChannelIcon} {privateChannelItems} diff --git a/webapp/components/sidebar_right_menu.jsx b/webapp/components/sidebar_right_menu.jsx index 2cf758f008..25136e8bc6 100644 --- a/webapp/components/sidebar_right_menu.jsx +++ b/webapp/components/sidebar_right_menu.jsx @@ -186,10 +186,10 @@ export default class SidebarRightMenu extends React.Component { } if (global.window.mm_license.IsLicensed === 'true') { - if (global.window.mm_config.RestrictTeamInvite === Constants.TEAM_INVITE_SYSTEM_ADMIN && !isSystemAdmin) { + if (global.window.mm_config.RestrictTeamInvite === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) { teamLink = null; inviteLink = null; - } else if (global.window.mm_config.RestrictTeamInvite === Constants.TEAM_INVITE_TEAM_ADMIN && !isAdmin) { + } else if (global.window.mm_config.RestrictTeamInvite === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) { teamLink = null; inviteLink = null; } diff --git a/webapp/components/tutorial/tutorial_intro_screens.jsx b/webapp/components/tutorial/tutorial_intro_screens.jsx index b0d831d96d..639fa07b26 100644 --- a/webapp/components/tutorial/tutorial_intro_screens.jsx +++ b/webapp/components/tutorial/tutorial_intro_screens.jsx @@ -108,7 +108,7 @@ export default class TutorialIntroScreens extends React.Component { let inviteModalLink; let inviteText; - if (global.window.mm_license.IsLicensed !== 'true' || global.window.mm_config.RestrictTeamInvite === Constants.TEAM_INVITE_ALL) { + if (global.window.mm_license.IsLicensed !== 'true' || global.window.mm_config.RestrictTeamInvite === Constants.PERMISSIONS_ALL) { if (team.type === Constants.INVITE_TEAM) { inviteModalLink = (
    Selecting \"Team and System Admins\" hides the email invitation and team invite link in the Main Menu from users who are not Team or System Admins. Note: If \"Get Team Invite Link\" is used to share a link, it will need to be regenerated after the desired users joined the team.

    Selecting \"System Admins\" hides the email invitation and team invite link in the Main Menu from users who are not System Admins. Note: If \"Get Team Invite Link\" is used to share a link, it will need to be regenerated after the desired users joined the team.", - "admin.general.policy.teamInviteSystemAdmin": "System Admins", + "admin.general.policy.permissionsAll": "All team members", + "admin.general.policy.permissionsAdmin": "Team and System Admins", + "admin.general.policy.permissionsSystemAdmin": "System Admins", "admin.general.policy.teamInviteTitle": "Enable sending team invites from:", + "admin.general.policy.teamInviteDescription": "Selecting \"All team members\" allows any team member to invite others using an email invitation or team invite link.

    Selecting \"Team and System Admins\" hides the email invitation and team invite link in the Main Menu from users who are not Team or System Admins. Note: If \"Get Team Invite Link\" is used to share a link, it will need to be regenerated after the desired users joined the team.

    Selecting \"System Admins\" hides the email invitation and team invite link in the Main Menu from users who are not System Admins. Note: If \"Get Team Invite Link\" is used to share a link, it will need to be regenerated after the desired users joined the team.", + "admin.general.policy.restrictPublicChannelManagementTitle": "Enable public channel management permissions for:", + "admin.general.policy.restrictPublicChannelManagementDescription": "Selecting \"All team members\" allows any team members to create, delete, rename, and set the header or purpose for public channels.

    Selecting \"Team and System Admins\" restricts channel management permissions for public channels to Team and System Admins, including creating, deleting, renaming, and setting the channel header or purpose.

    Selecting \"System Admins\" restricts channel management permissions for public channels to System Admins, including creating, deleting, renaming, and setting the channel header or purpose.", + "admin.general.policy.restrictPrivateChannelManagementTitle": "Enable private group management permissions for:", + "admin.general.policy.restrictPrivateChannelManagementDescription": "Selecting \"All team members\" allows any team members to create, delete, rename, and set the header or purpose for private groups.

    Selecting \"Team and System Admins\" restricts group management permissions for private groups to Team and System Admins, including creating, deleting, renaming, and setting the group header or purpose.

    Selecting \"System Admins\" restricts group management permissions for private groups to System Admins, including creating, deleting, renaming, and setting the group header or purpose.", "admin.general.privacy": "Privacy", "admin.general.usersAndTeams": "Users and Teams", "admin.gitab.clientSecretDescription": "Obtain this value via the instructions above for logging into GitLab.", diff --git a/webapp/stores/channel_store.jsx b/webapp/stores/channel_store.jsx index b65ec330c4..dc25778116 100644 --- a/webapp/stores/channel_store.jsx +++ b/webapp/stores/channel_store.jsx @@ -53,54 +53,70 @@ class ChannelStoreClass extends EventEmitter { this.extraInfos = {}; this.unreadCounts = {}; } + get POST_MODE_CHANNEL() { return 1; } + get POST_MODE_FOCUS() { return 2; } + emitChange() { this.emit(CHANGE_EVENT); } + addChangeListener(callback) { this.on(CHANGE_EVENT, callback); } + removeChangeListener(callback) { this.removeListener(CHANGE_EVENT, callback); } + emitMoreChange() { this.emit(MORE_CHANGE_EVENT); } + addMoreChangeListener(callback) { this.on(MORE_CHANGE_EVENT, callback); } + removeMoreChangeListener(callback) { this.removeListener(MORE_CHANGE_EVENT, callback); } + emitExtraInfoChange() { this.emit(EXTRA_INFO_EVENT); } + addExtraInfoChangeListener(callback) { this.on(EXTRA_INFO_EVENT, callback); } + removeExtraInfoChangeListener(callback) { this.removeListener(EXTRA_INFO_EVENT, callback); } emitLeave(id) { this.emit(LEAVE_EVENT, id); } + addLeaveListener(callback) { this.on(LEAVE_EVENT, callback); } + removeLeaveListener(callback) { this.removeListener(LEAVE_EVENT, callback); } + findFirstBy(field, value) { return this.doFindFirst(field, value, this.getChannels()); } + findFirstMoreBy(field, value) { return this.doFindFirst(field, value, this.getMoreChannels()); } + doFindFirst(field, value, channels) { for (var i = 0; i < channels.length; i++) { if (channels[i][field] === value) { @@ -110,33 +126,43 @@ class ChannelStoreClass extends EventEmitter { return null; } + get(id) { return this.findFirstBy('id', id); } + getMember(id) { return this.getAllMembers()[id]; } + getByName(name) { return this.findFirstBy('name', name); } + getByDisplayName(displayName) { return this.findFirstBy('display_name', displayName); } + getMoreByName(name) { return this.findFirstMoreBy('name', name); } + getAll() { return this.getChannels(); } + getAllMembers() { return this.getChannelMembers(); } + getMoreAll() { return this.getMoreChannels(); } + setCurrentId(id) { this.currentId = id; } + resetCounts(id) { const cm = this.channelMembers; for (var cmid in cm) { @@ -151,9 +177,11 @@ class ChannelStoreClass extends EventEmitter { } } } + getCurrentId() { return this.currentId; } + getCurrent() { var currentId = this.getCurrentId(); @@ -163,6 +191,7 @@ class ChannelStoreClass extends EventEmitter { return null; } + getCurrentMember() { var currentId = this.getCurrentId(); @@ -172,15 +201,18 @@ class ChannelStoreClass extends EventEmitter { return null; } + setChannelMember(member) { var members = this.getChannelMembers(); members[member.channel_id] = member; this.storeChannelMembers(members); this.emitChange(); } + getCurrentExtraInfo() { return this.getExtraInfo(this.getCurrentId()); } + getExtraInfo(channelId) { var extra = null; @@ -197,6 +229,7 @@ class ChannelStoreClass extends EventEmitter { return extra; } + pStoreChannel(channel) { var channels = this.getChannels(); var found; @@ -220,35 +253,45 @@ class ChannelStoreClass extends EventEmitter { channels.sort(Utils.sortByDisplayName); this.storeChannels(channels); } + storeChannels(channels) { this.channels = channels; } + getChannels() { return this.channels; } + pStoreChannelMember(channelMember) { var members = this.getChannelMembers(); members[channelMember.channel_id] = channelMember; this.storeChannelMembers(members); } + storeChannelMembers(channelMembers) { this.channelMembers = channelMembers; } + getChannelMembers() { return this.channelMembers; } + storeMoreChannels(channels) { this.moreChannels = channels; } + getMoreChannels() { return this.moreChannels; } + storeExtraInfos(extraInfos) { this.extraInfos = extraInfos; } + getExtraInfos() { return this.extraInfos; } + isDefault(channel) { return channel.name === Constants.DEFAULT_CHANNEL; } diff --git a/webapp/utils/channel_intro_messages.jsx b/webapp/utils/channel_intro_messages.jsx index 043894b7b2..50d12ed42d 100644 --- a/webapp/utils/channel_intro_messages.jsx +++ b/webapp/utils/channel_intro_messages.jsx @@ -114,9 +114,9 @@ export function createDefaultIntroMessage(channel) { const isSystemAdmin = UserStore.isSystemAdminForCurrentUser(); if (global.window.mm_license.IsLicensed === 'true') { - if (global.window.mm_config.RestrictTeamInvite === Constants.TEAM_INVITE_SYSTEM_ADMIN && !isSystemAdmin) { + if (global.window.mm_config.RestrictTeamInvite === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) { inviteModalLink = null; - } else if (global.window.mm_config.RestrictTeamInvite === Constants.TEAM_INVITE_TEAM_ADMIN && !isAdmin) { + } else if (global.window.mm_config.RestrictTeamInvite === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) { inviteModalLink = null; } } diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx index 1b0fa63741..0191edcf08 100644 --- a/webapp/utils/constants.jsx +++ b/webapp/utils/constants.jsx @@ -762,7 +762,7 @@ export default { POST_COLLAPSE_TIMEOUT: 1000 * 60 * 5, // five minutes LICENSE_EXPIRY_NOTIFICATION: 1000 * 60 * 60 * 24 * 15, // 15 days LICENSE_GRACE_PERIOD: 1000 * 60 * 60 * 24 * 15, // 15 days - TEAM_INVITE_ALL: 'all', - TEAM_INVITE_TEAM_ADMIN: 'team_admin', - TEAM_INVITE_SYSTEM_ADMIN: 'system_admin' + PERMISSIONS_ALL: 'all', + PERMISSIONS_TEAM_ADMIN: 'team_admin', + PERMISSIONS_SYSTEM_ADMIN: 'system_admin' };