Skip to content

Commit

Permalink
Disable reactions / files attachments / linked channels by default (#496
Browse files Browse the repository at this point in the history
)

* disable reactions by default

* disable file attachments by default

* disable linked channels by default

* Update plugin.json

Co-authored-by: Doug Lauder <wiggin77@warpmail.net>

---------

Co-authored-by: Doug Lauder <wiggin77@warpmail.net>
  • Loading branch information
lieut-data and wiggin77 committed Feb 14, 2024
1 parent edd0095 commit 373f910
Show file tree
Hide file tree
Showing 15 changed files with 675 additions and 62 deletions.
18 changes: 18 additions & 0 deletions plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,24 @@
"type": "bool",
"help_text": "Sync direct and group messages where any of the user in the conversation is a real Mattermost user connected to MS Teams account",
"default": false
},{
"key": "syncLinkedChannels",
"display_name": "Sync linked channels",
"type": "bool",
"help_text": "Sync messages from channels linked between Mattermost and MS Teams",
"default": false
},{
"key": "syncReactions",
"display_name": "Sync reactions",
"type": "bool",
"help_text": "Sync reactions on messages",
"default": false
},{
"key": "syncFileAttachments",
"display_name": "Sync file attachments",
"type": "bool",
"help_text": "Sync file attachments on messages",
"default": false
},{
"key": "enabledTeams",
"display_name": "Enabled Teams",
Expand Down
54 changes: 29 additions & 25 deletions server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
const msteamsCommand = "msteams-sync"
const commandWaitingMessage = "Please wait while your request is being processed."

func (p *Plugin) createMsteamsSyncCommand() *model.Command {
func (p *Plugin) createMsteamsSyncCommand(syncLinkedChannels bool) *model.Command {
iconData, err := command.GetIconData(p.API, "assets/msteams-sync-icon.svg")
if err != nil {
p.API.LogWarn("Unable to get the MS Teams icon for the slash command")
Expand All @@ -29,7 +29,7 @@ func (p *Plugin) createMsteamsSyncCommand() *model.Command {
AutoCompleteHint: "[command]",
Username: botUsername,
DisplayName: botDisplayName,
AutocompleteData: getAutocompleteData(),
AutocompleteData: getAutocompleteData(syncLinkedChannels),
AutocompleteIconData: iconData,
}
}
Expand All @@ -51,23 +51,25 @@ func (p *Plugin) sendBotEphemeralPost(userID, channelID, message string) {
})
}

func getAutocompleteData() *model.AutocompleteData {
func getAutocompleteData(syncLinkedChannels bool) *model.AutocompleteData {
cmd := model.NewAutocompleteData(msteamsCommand, "[command]", "Manage MS Teams linked channels")

link := model.NewAutocompleteData("link", "[msteams-team-id] [msteams-channel-id]", "Link current channel to a MS Teams channel")
link.AddDynamicListArgument("[msteams-team-id]", getAutocompletePath("teams"), true)
link.AddDynamicListArgument("[msteams-channel-id]", getAutocompletePath("channels"), true)
cmd.AddCommand(link)
if syncLinkedChannels {
link := model.NewAutocompleteData("link", "[msteams-team-id] [msteams-channel-id]", "Link current channel to a MS Teams channel")
link.AddDynamicListArgument("[msteams-team-id]", getAutocompletePath("teams"), true)
link.AddDynamicListArgument("[msteams-channel-id]", getAutocompletePath("channels"), true)
cmd.AddCommand(link)

unlink := model.NewAutocompleteData("unlink", "", "Unlink the current channel from the MS Teams channel")
cmd.AddCommand(unlink)
unlink := model.NewAutocompleteData("unlink", "", "Unlink the current channel from the MS Teams channel")
cmd.AddCommand(unlink)

show := model.NewAutocompleteData("show", "", "Show MS Teams linked channel")
cmd.AddCommand(show)
show := model.NewAutocompleteData("show", "", "Show MS Teams linked channel")
cmd.AddCommand(show)

showLinks := model.NewAutocompleteData("show-links", "", "Show all MS Teams linked channels")
showLinks.RoleID = model.SystemAdminRoleId
cmd.AddCommand(showLinks)
showLinks := model.NewAutocompleteData("show-links", "", "Show all MS Teams linked channels")
showLinks.RoleID = model.SystemAdminRoleId
cmd.AddCommand(showLinks)
}

connect := model.NewAutocompleteData("connect", "", "Connect your Mattermost account to your MS Teams account")
cmd.AddCommand(connect)
Expand Down Expand Up @@ -108,20 +110,22 @@ func (p *Plugin) ExecuteCommand(_ *plugin.Context, args *model.CommandArgs) (*mo
return &model.CommandResponse{}, nil
}

if action == "link" {
return p.executeLinkCommand(args, parameters)
}
if p.getConfiguration().SyncLinkedChannels {
if action == "link" {
return p.executeLinkCommand(args, parameters)
}

if action == "unlink" {
return p.executeUnlinkCommand(args)
}
if action == "unlink" {
return p.executeUnlinkCommand(args)
}

if action == "show" {
return p.executeShowCommand(args)
}
if action == "show" {
return p.executeShowCommand(args)
}

if action == "show-links" {
return p.executeShowLinksCommand(args)
if action == "show-links" {
return p.executeShowLinksCommand(args)
}
}

if action == "connect" {
Expand Down
77 changes: 73 additions & 4 deletions server/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1002,11 +1002,13 @@ func TestExecuteConnectBotCommand(t *testing.T) {

func TestGetAutocompleteData(t *testing.T) {
for _, testCase := range []struct {
description string
autocompleteData *model.AutocompleteData
description string
syncLinkedChannels bool
autocompleteData *model.AutocompleteData
}{
{
description: "Successfully get all auto complete data",
description: "Successfully get all auto complete data",
syncLinkedChannels: true,
autocompleteData: &model.AutocompleteData{
Trigger: "msteams-sync",
Hint: "[command]",
Expand Down Expand Up @@ -1117,9 +1119,76 @@ func TestGetAutocompleteData(t *testing.T) {
},
},
},
{
description: "Successfully get all auto complete data",
syncLinkedChannels: false,
autocompleteData: &model.AutocompleteData{
Trigger: "msteams-sync",
Hint: "[command]",
HelpText: "Manage MS Teams linked channels",
RoleID: model.SystemUserRoleId,
Arguments: []*model.AutocompleteArg{},
SubCommands: []*model.AutocompleteData{
{
Trigger: "connect",
HelpText: "Connect your Mattermost account to your MS Teams account",
RoleID: model.SystemUserRoleId,
Arguments: []*model.AutocompleteArg{},
SubCommands: []*model.AutocompleteData{},
},
{
Trigger: "disconnect",
HelpText: "Disconnect your Mattermost account from your MS Teams account",
RoleID: model.SystemUserRoleId,
Arguments: []*model.AutocompleteArg{},
SubCommands: []*model.AutocompleteData{},
},
{
Trigger: "connect-bot",
HelpText: "Connect the bot account (only system admins can do this)",
RoleID: model.SystemAdminRoleId,
Arguments: []*model.AutocompleteArg{},
SubCommands: []*model.AutocompleteData{},
},
{
Trigger: "disconnect-bot",
HelpText: "Disconnect the bot account (only system admins can do this)",
RoleID: model.SystemAdminRoleId,
Arguments: []*model.AutocompleteArg{},
SubCommands: []*model.AutocompleteData{},
},
{
Trigger: "promote",
HelpText: "Promote a user from synthetic user account to regular mattermost account",
RoleID: model.SystemAdminRoleId,
Arguments: []*model.AutocompleteArg{
{
HelpText: "Username of the existing mattermost user",
Type: "TextInput",
Required: true,
Data: &model.AutocompleteTextArg{
Hint: "username",
Pattern: `^[a-z0-9\.\-_:]+$`,
},
},
{
HelpText: "The new username after the user is promoted",
Type: "TextInput",
Required: true,
Data: &model.AutocompleteTextArg{
Hint: "new username",
Pattern: `^[a-z0-9\.\-_:]+$`,
},
},
},
SubCommands: []*model.AutocompleteData{},
},
},
},
},
} {
t.Run(testCase.description, func(t *testing.T) {
autocompleteData := getAutocompleteData()
autocompleteData := getAutocompleteData(testCase.syncLinkedChannels)
assert.Equal(t, testCase.autocompleteData, autocompleteData)
})
}
Expand Down
3 changes: 3 additions & 0 deletions server/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ type configuration struct {
WebhookSecret string `json:"webhooksecret"`
EnabledTeams string `json:"enabledteams"`
SyncDirectMessages bool `json:"syncdirectmessages"`
SyncLinkedChannels bool `json:"synclinkedchannels"`
SyncReactions bool `json:"syncreactions"`
SyncFileAttachments bool `json:"syncfileattachments"`
SyncUsers int `json:"syncusers"`
SyncGuestUsers bool `json:"syncGuestUsers"`
CertificatePublic string `json:"certificatepublic"`
Expand Down
4 changes: 4 additions & 0 deletions server/handlers/attachments.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ func (ah *ActivityHandler) handleAttachments(channelID, userID, text string, msg
continue
}

if !ah.plugin.GetSyncFileAttachments() {
continue
}

// handle the download
var attachmentData []byte
var err error
Expand Down
32 changes: 32 additions & 0 deletions server/handlers/attachments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,36 @@ func TestHandleAttachments(t *testing.T) {
expectedParentID string
expectedError bool
}{
{
description: "File attachments disabled by configuration",
setupPlugin: func(p *mocksPlugin.PluginIface, mockAPI *plugintest.API, client *mocksClient.Client, store *mocksStore.Store, mockmetrics *mocksMetrics.Metrics) {
p.On("GetSyncFileAttachments").Return(false).Maybe()
p.On("GetClientForApp").Return(client).Maybe()
p.On("GetAPI").Return(mockAPI).Maybe()
p.On("GetMetrics").Return(mockmetrics).Maybe()
},
setupAPI: func(mockAPI *plugintest.API) {
mockAPI.On("GetConfig").Return(&model.Config{
FileSettings: model.FileSettings{
MaxFileSize: model.NewInt64(5),
},
})
mockAPI.On("UploadFile", []byte{}, testutils.GetChannelID(), "mock-name").Return(&model.FileInfo{
Id: testutils.GetID(),
}, nil)
},
setupClient: func(client *mocksClient.Client) {
},
setupMetrics: func(mockmetrics *mocksMetrics.Metrics) {
},
attachments: []clientmodels.Attachment{},
expectedText: "mock-text",
expectedAttachmentIDsCount: 0,
},
{
description: "Successfully handled attachments",
setupPlugin: func(p *mocksPlugin.PluginIface, mockAPI *plugintest.API, client *mocksClient.Client, store *mocksStore.Store, mockmetrics *mocksMetrics.Metrics) {
p.On("GetSyncFileAttachments").Return(true).Maybe()
p.On("GetClientForApp").Return(client).Maybe()
p.On("GetAPI").Return(mockAPI).Maybe()
p.On("GetMaxSizeForCompleteDownload").Return(1).Times(1)
Expand Down Expand Up @@ -302,6 +329,7 @@ func TestHandleAttachments(t *testing.T) {
{
description: "Client is nil",
setupPlugin: func(p *mocksPlugin.PluginIface, mockAPI *plugintest.API, client *mocksClient.Client, store *mocksStore.Store, mockmetrics *mocksMetrics.Metrics) {
p.On("GetSyncFileAttachments").Return(true).Maybe()
p.On("GetClientForApp").Return(nil)
p.On("GetAPI").Return(mockAPI).Maybe()
},
Expand All @@ -318,6 +346,7 @@ func TestHandleAttachments(t *testing.T) {
{
description: "Error uploading the file",
setupPlugin: func(p *mocksPlugin.PluginIface, mockAPI *plugintest.API, client *mocksClient.Client, store *mocksStore.Store, mockmetrics *mocksMetrics.Metrics) {
p.On("GetSyncFileAttachments").Return(true).Maybe()
p.On("GetClientForApp").Return(client).Maybe()
p.On("GetAPI").Return(mockAPI).Maybe()
p.On("GetMaxSizeForCompleteDownload").Return(1).Times(1)
Expand Down Expand Up @@ -348,6 +377,7 @@ func TestHandleAttachments(t *testing.T) {
{
description: "Number of attachments are greater than 10",
setupPlugin: func(p *mocksPlugin.PluginIface, mockAPI *plugintest.API, client *mocksClient.Client, store *mocksStore.Store, mockmetrics *mocksMetrics.Metrics) {
p.On("GetSyncFileAttachments").Return(true).Maybe()
p.On("GetClientForApp").Return(client).Maybe()
p.On("GetAPI").Return(mockAPI).Maybe()
p.On("GetMaxSizeForCompleteDownload").Return(1).Times(10)
Expand Down Expand Up @@ -378,6 +408,7 @@ func TestHandleAttachments(t *testing.T) {
{
description: "Attachment type code snippet",
setupPlugin: func(p *mocksPlugin.PluginIface, mockAPI *plugintest.API, client *mocksClient.Client, store *mocksStore.Store, mockmetrics *mocksMetrics.Metrics) {
p.On("GetSyncFileAttachments").Return(true).Maybe()
p.On("GetClientForApp").Return(client).Maybe()
p.On("GetMetrics").Return(mockmetrics).Maybe()
},
Expand Down Expand Up @@ -405,6 +436,7 @@ func TestHandleAttachments(t *testing.T) {
{
description: "Attachment type message reference",
setupPlugin: func(p *mocksPlugin.PluginIface, mockAPI *plugintest.API, client *mocksClient.Client, store *mocksStore.Store, mockmetrics *mocksMetrics.Metrics) {
p.On("GetSyncFileAttachments").Return(true).Maybe()
p.On("GetMetrics").Return(mockmetrics).Maybe()
p.On("GetClientForApp").Return(client).Maybe()
p.On("GetStore").Return(store, nil)
Expand Down
6 changes: 6 additions & 0 deletions server/handlers/getters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ type pluginMock struct {
api plugin.API
store store.Store
syncDirectMessages bool
syncLinkedChannels bool
syncReactions bool
syncFileAttachments bool
syncGuestUsers bool
maxSizeForCompleteDownload int
bufferSizeForStreaming int
Expand All @@ -40,7 +43,10 @@ type pluginMock struct {

func (pm *pluginMock) GetAPI() plugin.API { return pm.api }
func (pm *pluginMock) GetStore() store.Store { return pm.store }
func (pm *pluginMock) GetSyncLinkedChannels() bool { return pm.syncLinkedChannels }
func (pm *pluginMock) GetSyncDirectMessages() bool { return pm.syncDirectMessages }
func (pm *pluginMock) GetSyncFileAttachments() bool { return pm.syncFileAttachments }
func (pm *pluginMock) GetSyncReactions() bool { return pm.syncReactions }
func (pm *pluginMock) GetSyncGuestUsers() bool { return pm.syncGuestUsers }
func (pm *pluginMock) GetMaxSizeForCompleteDownload() int { return pm.maxSizeForCompleteDownload }
func (pm *pluginMock) GetBufferSizeForStreaming() int { return pm.bufferSizeForStreaming }
Expand Down
17 changes: 17 additions & 0 deletions server/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ type PluginIface interface {
GetStore() store.Store
GetMetrics() metrics.Metrics
GetSyncDirectMessages() bool
GetSyncLinkedChannels() bool
GetSyncReactions() bool
GetSyncFileAttachments() bool
GetSyncGuestUsers() bool
GetMaxSizeForCompleteDownload() int
GetBufferSizeForStreaming() int
Expand Down Expand Up @@ -310,6 +313,11 @@ func (ah *ActivityHandler) handleCreatedActivity(msg *clientmodels.Message, subs
}
senderID, _ = ah.plugin.GetStore().TeamsToMattermostUserID(msg.UserID)
} else {
if !ah.plugin.GetSyncLinkedChannels() {
// Skipping because linked channels are disabled
return metrics.DiscardedReasonLinkedChannelsDisabled
}

senderID, _ = ah.getOrCreateSyntheticUser(msteamsUser, true)
channelLink, _ := ah.plugin.GetStore().GetLinkByMSTeamsChannelID(msg.TeamID, msg.ChannelID)
if channelLink != nil {
Expand Down Expand Up @@ -389,6 +397,11 @@ func (ah *ActivityHandler) handleUpdatedActivity(msg *clientmodels.Message, subs

channelID := ""
if chat == nil {
if !ah.plugin.GetSyncLinkedChannels() {
// Skipping because linked channels are disabled
return metrics.DiscardedReasonLinkedChannelsDisabled
}

var channelLink *storemodels.ChannelLink
channelLink, err = ah.plugin.GetStore().GetLinkByMSTeamsChannelID(msg.TeamID, msg.ChannelID)
if err != nil || channelLink == nil {
Expand Down Expand Up @@ -456,6 +469,10 @@ func (ah *ActivityHandler) handleUpdatedActivity(msg *clientmodels.Message, subs
}

func (ah *ActivityHandler) handleReactions(postID, channelID string, isDirectMessage bool, reactions []clientmodels.Reaction) {
if !ah.plugin.GetSyncReactions() {
return
}

postReactions, appErr := ah.plugin.GetAPI().GetReactions(postID)
if appErr != nil {
return
Expand Down
Loading

0 comments on commit 373f910

Please sign in to comment.