diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/app.go | 8 | ||||
-rw-r--r-- | app/apptestlib.go | 18 | ||||
-rw-r--r-- | app/channel.go | 24 | ||||
-rw-r--r-- | app/command_channel_header.go | 22 | ||||
-rw-r--r-- | app/command_channel_header_test.go | 82 | ||||
-rw-r--r-- | app/command_groupmsg.go | 18 | ||||
-rw-r--r-- | app/command_groupmsg_test.go | 60 | ||||
-rw-r--r-- | app/command_invite_people.go | 14 | ||||
-rw-r--r-- | app/command_invite_people_test.go | 42 | ||||
-rw-r--r-- | app/command_msg.go | 4 | ||||
-rw-r--r-- | app/command_msg_test.go | 34 | ||||
-rw-r--r-- | app/email.go | 48 | ||||
-rw-r--r-- | app/team.go | 6 |
13 files changed, 368 insertions, 12 deletions
diff --git a/app/app.go b/app/app.go index 96b9b6d13..6f98d4234 100644 --- a/app/app.go +++ b/app/app.go @@ -17,6 +17,7 @@ import ( "github.com/gorilla/mux" "github.com/pkg/errors" + "github.com/throttled/throttled" "github.com/mattermost/mattermost-server/einterfaces" ejobs "github.com/mattermost/mattermost-server/einterfaces/jobs" @@ -46,7 +47,8 @@ type App struct { IsPluginSandboxSupported bool pluginStatuses map[string]*model.PluginStatus - EmailBatching *EmailBatchingJob + EmailBatching *EmailBatchingJob + EmailRateLimiter *throttled.GCRARateLimiter Hubs []*Hub HubsStopCheckingForDeadlock chan bool @@ -185,6 +187,10 @@ func New(options ...Option) (outApp *App, outErr error) { }) + if err := app.SetupInviteEmailRateLimiting(); err != nil { + return nil, err + } + mlog.Info("Server is initializing...") app.initEnterprise() diff --git a/app/apptestlib.go b/app/apptestlib.go index 818b21183..43d425e16 100644 --- a/app/apptestlib.go +++ b/app/apptestlib.go @@ -206,6 +206,10 @@ func (me *TestHelper) CreateChannel(team *model.Team) *model.Channel { return me.createChannel(team, model.CHANNEL_OPEN) } +func (me *TestHelper) CreatePrivateChannel(team *model.Team) *model.Channel { + return me.createChannel(team, model.CHANNEL_PRIVATE) +} + func (me *TestHelper) createChannel(team *model.Team, channelType string) *model.Channel { id := model.NewId() @@ -266,6 +270,20 @@ func (me *TestHelper) CreateDmChannel(user *model.User) *model.Channel { return channel } +func (me *TestHelper) CreateGroupChannel(user1 *model.User, user2 *model.User) *model.Channel { + utils.DisableDebugLogForTest() + var err *model.AppError + var channel *model.Channel + if channel, err = me.App.CreateGroupChannel([]string{me.BasicUser.Id, user1.Id, user2.Id}, me.BasicUser.Id); err != nil { + mlog.Error(err.Error()) + + time.Sleep(time.Second) + panic(err) + } + utils.EnableDebugLogForTest() + return channel +} + func (me *TestHelper) CreatePost(channel *model.Channel) *model.Post { id := model.NewId() diff --git a/app/channel.go b/app/channel.go index eee27a6de..5607601c6 100644 --- a/app/channel.go +++ b/app/channel.go @@ -340,6 +340,30 @@ func (a *App) createGroupChannel(userIds []string, creatorId string) (*model.Cha } } +func (a *App) GetGroupChannel(userIds []string) (*model.Channel, *model.AppError) { + if len(userIds) > model.CHANNEL_GROUP_MAX_USERS || len(userIds) < model.CHANNEL_GROUP_MIN_USERS { + return nil, model.NewAppError("GetGroupChannel", "api.channel.create_group.bad_size.app_error", nil, "", http.StatusBadRequest) + } + + var users []*model.User + if result := <-a.Srv.Store.User().GetProfileByIds(userIds, true); result.Err != nil { + return nil, result.Err + } else { + users = result.Data.([]*model.User) + } + + if len(users) != len(userIds) { + return nil, model.NewAppError("GetGroupChannel", "api.channel.create_group.bad_user.app_error", nil, "user_ids="+model.ArrayToJson(userIds), http.StatusBadRequest) + } + + channel, err := a.GetChannelByName(model.GetGroupNameFromUserIds(userIds), "") + if err != nil { + return nil, err + } + + return channel, nil +} + func (a *App) UpdateChannel(channel *model.Channel) (*model.Channel, *model.AppError) { if result := <-a.Srv.Store.Channel().Update(channel); result.Err != nil { return nil, result.Err diff --git a/app/command_channel_header.go b/app/command_channel_header.go index 63a9250a7..100135f48 100644 --- a/app/command_channel_header.go +++ b/app/command_channel_header.go @@ -40,11 +40,25 @@ func (me *HeaderProvider) DoCommand(a *App, args *model.CommandArgs, message str return &model.CommandResponse{Text: args.T("api.command_channel_header.channel.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} } - if channel.Type == model.CHANNEL_OPEN && !a.SessionHasPermissionToChannel(args.Session, args.ChannelId, model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES) { - return &model.CommandResponse{Text: args.T("api.command_channel_header.permission.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } + switch channel.Type { + case model.CHANNEL_OPEN: + if !a.SessionHasPermissionToChannel(args.Session, args.ChannelId, model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES) { + return &model.CommandResponse{Text: args.T("api.command_channel_header.permission.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + + case model.CHANNEL_PRIVATE: + if !a.SessionHasPermissionToChannel(args.Session, args.ChannelId, model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES) { + return &model.CommandResponse{Text: args.T("api.command_channel_header.permission.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + + case model.CHANNEL_GROUP, model.CHANNEL_DIRECT: + // Modifying the header is not linked to any specific permission for group/dm channels, so just check for membership. + channelMember, err := a.GetChannelMember(args.ChannelId, args.Session.UserId) + if err != nil || channelMember == nil { + return &model.CommandResponse{Text: args.T("api.command_channel_header.permission.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } - if channel.Type == model.CHANNEL_PRIVATE && !a.SessionHasPermissionToChannel(args.Session, args.ChannelId, model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES) { + default: return &model.CommandResponse{Text: args.T("api.command_channel_header.permission.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} } diff --git a/app/command_channel_header_test.go b/app/command_channel_header_test.go index 2a6151fed..21735e044 100644 --- a/app/command_channel_header_test.go +++ b/app/command_channel_header_test.go @@ -12,6 +12,8 @@ func TestHeaderProviderDoCommand(t *testing.T) { defer th.TearDown() hp := HeaderProvider{} + + // Try a public channel *with* permission. args := &model.CommandArgs{ T: func(s string, args ...interface{}) string { return s }, ChannelId: th.BasicChannel.Id, @@ -25,4 +27,84 @@ func TestHeaderProviderDoCommand(t *testing.T) { actual := hp.DoCommand(th.App, args, msg).Text assert.Equal(t, expected, actual) } + + // Try a public channel *without* permission. + args = &model.CommandArgs{ + T: func(s string, args ...interface{}) string { return s }, + ChannelId: th.BasicChannel.Id, + Session: model.Session{UserId: th.BasicUser.Id, TeamMembers: []*model.TeamMember{{TeamId: th.BasicTeam.Id, Roles: ""}}}, + } + + actual := hp.DoCommand(th.App, args, "hello").Text + assert.Equal(t, "api.command_channel_header.permission.app_error", actual) + + // Try a private channel *with* permission. + privateChannel := th.CreatePrivateChannel(th.BasicTeam) + + args = &model.CommandArgs{ + T: func(s string, args ...interface{}) string { return s }, + ChannelId: privateChannel.Id, + Session: model.Session{UserId: th.BasicUser.Id, TeamMembers: []*model.TeamMember{{TeamId: th.BasicTeam.Id, Roles: model.TEAM_USER_ROLE_ID}}}, + } + + actual = hp.DoCommand(th.App, args, "hello").Text + assert.Equal(t, "", actual) + + // Try a private channel *without* permission. + args = &model.CommandArgs{ + T: func(s string, args ...interface{}) string { return s }, + ChannelId: privateChannel.Id, + Session: model.Session{UserId: th.BasicUser.Id, TeamMembers: []*model.TeamMember{{TeamId: th.BasicTeam.Id, Roles: ""}}}, + } + + actual = hp.DoCommand(th.App, args, "hello").Text + assert.Equal(t, "api.command_channel_header.permission.app_error", actual) + + // Try a group channel *with* being a member. + user1 := th.CreateUser() + user2 := th.CreateUser() + user3 := th.CreateUser() + + groupChannel := th.CreateGroupChannel(user1, user2) + + args = &model.CommandArgs{ + T: func(s string, args ...interface{}) string { return s }, + ChannelId: groupChannel.Id, + Session: model.Session{UserId: th.BasicUser.Id, TeamMembers: []*model.TeamMember{{TeamId: th.BasicTeam.Id, Roles: ""}}}, + } + + actual = hp.DoCommand(th.App, args, "hello").Text + assert.Equal(t, "", actual) + + // Try a group channel *without* being a member. + args = &model.CommandArgs{ + T: func(s string, args ...interface{}) string { return s }, + ChannelId: groupChannel.Id, + Session: model.Session{UserId: user3.Id, TeamMembers: []*model.TeamMember{{TeamId: th.BasicTeam.Id, Roles: ""}}}, + } + + actual = hp.DoCommand(th.App, args, "hello").Text + assert.Equal(t, "api.command_channel_header.permission.app_error", actual) + + // Try a direct channel *with* being a member. + directChannel := th.CreateDmChannel(user1) + + args = &model.CommandArgs{ + T: func(s string, args ...interface{}) string { return s }, + ChannelId: directChannel.Id, + Session: model.Session{UserId: th.BasicUser.Id, TeamMembers: []*model.TeamMember{{TeamId: th.BasicTeam.Id, Roles: ""}}}, + } + + actual = hp.DoCommand(th.App, args, "hello").Text + assert.Equal(t, "", actual) + + // Try a direct channel *without* being a member. + args = &model.CommandArgs{ + T: func(s string, args ...interface{}) string { return s }, + ChannelId: directChannel.Id, + Session: model.Session{UserId: user2.Id, TeamMembers: []*model.TeamMember{{TeamId: th.BasicTeam.Id, Roles: ""}}}, + } + + actual = hp.DoCommand(th.App, args, "hello").Text + assert.Equal(t, "api.command_channel_header.permission.app_error", actual) } diff --git a/app/command_groupmsg.go b/app/command_groupmsg.go index 0e783e1a8..9ec84fda0 100644 --- a/app/command_groupmsg.go +++ b/app/command_groupmsg.go @@ -93,10 +93,20 @@ func (me *groupmsgProvider) DoCommand(a *App, args *model.CommandArgs, message s } } - groupChannel, channelErr := a.CreateGroupChannel(targetUsersSlice, args.UserId) - if channelErr != nil { - mlog.Error(channelErr.Error()) - return &model.CommandResponse{Text: args.T("api.command_groupmsg.group_fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + var groupChannel *model.Channel + var channelErr *model.AppError + + if a.SessionHasPermissionTo(args.Session, model.PERMISSION_CREATE_GROUP_CHANNEL) { + groupChannel, channelErr = a.CreateGroupChannel(targetUsersSlice, args.UserId) + if channelErr != nil { + mlog.Error(channelErr.Error()) + return &model.CommandResponse{Text: args.T("api.command_groupmsg.group_fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + } else { + groupChannel, channelErr = a.GetGroupChannel(targetUsersSlice) + if channelErr != nil { + return &model.CommandResponse{Text: args.T("api.command_groupmsg.permission.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } } if len(parsedMessage) > 0 { diff --git a/app/command_groupmsg_test.go b/app/command_groupmsg_test.go index 610d2e446..422679525 100644 --- a/app/command_groupmsg_test.go +++ b/app/command_groupmsg_test.go @@ -2,6 +2,11 @@ package app import ( "testing" + + "github.com/nicksnyder/go-i18n/i18n" + "github.com/stretchr/testify/assert" + + "github.com/mattermost/mattermost-server/model" ) func TestGroupMsgUsernames(t *testing.T) { @@ -35,3 +40,58 @@ func TestGroupMsgUsernames(t *testing.T) { t.Fatal("error parsing different types of users") } } + +func TestGroupMsgProvider(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + user3 := th.CreateUser() + targetUsers := "@" + th.BasicUser2.Username + ",@" + user3.Username + " " + + team := th.CreateTeam() + th.LinkUserToTeam(th.BasicUser, team) + cmd := &groupmsgProvider{} + + // Check without permission to create a GM channel. + resp := cmd.DoCommand(th.App, &model.CommandArgs{ + T: i18n.IdentityTfunc(), + SiteURL: "http://test.url", + TeamId: team.Id, + UserId: th.BasicUser.Id, + Session: model.Session{ + Roles: "", + }, + }, targetUsers+"hello") + + channelName := model.GetGroupNameFromUserIds([]string{th.BasicUser.Id, th.BasicUser2.Id, user3.Id}) + assert.Equal(t, "api.command_groupmsg.permission.app_error", resp.Text) + assert.Equal(t, "", resp.GotoLocation) + + // Check with permission to create a GM channel. + resp = cmd.DoCommand(th.App, &model.CommandArgs{ + T: i18n.IdentityTfunc(), + SiteURL: "http://test.url", + TeamId: team.Id, + UserId: th.BasicUser.Id, + Session: model.Session{ + Roles: model.SYSTEM_USER_ROLE_ID, + }, + }, targetUsers+"hello") + + assert.Equal(t, "", resp.Text) + assert.Equal(t, "http://test.url/"+team.Name+"/channels/"+channelName, resp.GotoLocation) + + // Check without permission to post to an existing GM channel. + resp = cmd.DoCommand(th.App, &model.CommandArgs{ + T: i18n.IdentityTfunc(), + SiteURL: "http://test.url", + TeamId: team.Id, + UserId: th.BasicUser.Id, + Session: model.Session{ + Roles: "", + }, + }, targetUsers+"hello") + + assert.Equal(t, "", resp.Text) + assert.Equal(t, "http://test.url/"+team.Name+"/channels/"+channelName, resp.GotoLocation) +} diff --git a/app/command_invite_people.go b/app/command_invite_people.go index c3dc4f469..fe12a5684 100644 --- a/app/command_invite_people.go +++ b/app/command_invite_people.go @@ -28,7 +28,7 @@ func (me *InvitePeopleProvider) GetTrigger() string { func (me *InvitePeopleProvider) GetCommand(a *App, T goi18n.TranslateFunc) *model.Command { autoComplete := true - if !a.Config().EmailSettings.SendEmailNotifications || !*a.Config().TeamSettings.EnableUserCreation { + if !a.Config().EmailSettings.SendEmailNotifications || !*a.Config().TeamSettings.EnableUserCreation || !*a.Config().ServiceSettings.EnableEmailInvitations { autoComplete = false } return &model.Command{ @@ -41,6 +41,14 @@ func (me *InvitePeopleProvider) GetCommand(a *App, T goi18n.TranslateFunc) *mode } func (me *InvitePeopleProvider) DoCommand(a *App, args *model.CommandArgs, message string) *model.CommandResponse { + if !a.SessionHasPermissionToTeam(args.Session, args.TeamId, model.PERMISSION_INVITE_USER) { + return &model.CommandResponse{Text: args.T("api.command_invite_people.permission.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + + if !a.SessionHasPermissionToTeam(args.Session, args.TeamId, model.PERMISSION_ADD_USER_TO_TEAM) { + return &model.CommandResponse{Text: args.T("api.command_invite_people.permission.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + if !a.Config().EmailSettings.SendEmailNotifications { return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: args.T("api.command.invite_people.email_off")} } @@ -49,6 +57,10 @@ func (me *InvitePeopleProvider) DoCommand(a *App, args *model.CommandArgs, messa return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: args.T("api.command.invite_people.invite_off")} } + if !*a.Config().ServiceSettings.EnableEmailInvitations { + return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: args.T("api.command.invite_people.email_invitations_off")} + } + emailList := strings.Fields(message) for i := len(emailList) - 1; i >= 0; i-- { diff --git a/app/command_invite_people_test.go b/app/command_invite_people_test.go new file mode 100644 index 000000000..5cf7aa412 --- /dev/null +++ b/app/command_invite_people_test.go @@ -0,0 +1,42 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package app + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/mattermost/mattermost-server/model" +) + +func TestInvitePeopleProvider(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + enableEmailInvitations := *th.App.Config().ServiceSettings.EnableEmailInvitations + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableEmailInvitations = &enableEmailInvitations }) + }() + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableEmailInvitations = true }) + + cmd := InvitePeopleProvider{} + + // Test without required permissions + args := &model.CommandArgs{ + T: func(s string, args ...interface{}) string { return s }, + ChannelId: th.BasicChannel.Id, + TeamId: th.BasicTeam.Id, + UserId: th.BasicUser.Id, + Session: model.Session{UserId: th.BasicUser.Id, TeamMembers: []*model.TeamMember{{TeamId: th.BasicTeam.Id, Roles: ""}}}, + } + + actual := cmd.DoCommand(th.App, args, model.NewId()+"@simulator.amazonses.com") + assert.Equal(t, "api.command_invite_people.permission.app_error", actual.Text) + + // Test with required permissions. + args.Session.TeamMembers[0].Roles = model.TEAM_USER_ROLE_ID + actual = cmd.DoCommand(th.App, args, model.NewId()+"@simulator.amazonses.com") + assert.Equal(t, "api.command.invite_people.sent", actual.Text) +} diff --git a/app/command_msg.go b/app/command_msg.go index 6877c10d6..bf414210a 100644 --- a/app/command_msg.go +++ b/app/command_msg.go @@ -66,6 +66,10 @@ func (me *msgProvider) DoCommand(a *App, args *model.CommandArgs, message string targetChannelId := "" if channel := <-a.Srv.Store.Channel().GetByName(args.TeamId, channelName, true); channel.Err != nil { if channel.Err.Id == "store.sql_channel.get_by_name.missing.app_error" { + if !a.SessionHasPermissionTo(args.Session, model.PERMISSION_CREATE_DIRECT_CHANNEL) { + return &model.CommandResponse{Text: args.T("api.command_msg.permission.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + if directChannel, err := a.CreateDirectChannel(args.UserId, userProfile.Id); err != nil { mlog.Error(err.Error()) return &model.CommandResponse{Text: args.T("api.command_msg.dm_fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} diff --git a/app/command_msg_test.go b/app/command_msg_test.go index 22c4d07cf..9a8ca8eb0 100644 --- a/app/command_msg_test.go +++ b/app/command_msg_test.go @@ -19,13 +19,47 @@ func TestMsgProvider(t *testing.T) { team := th.CreateTeam() th.LinkUserToTeam(th.BasicUser, team) cmd := &msgProvider{} + + // Check without permission to create a DM channel. resp := cmd.DoCommand(th.App, &model.CommandArgs{ T: i18n.IdentityTfunc(), SiteURL: "http://test.url", TeamId: team.Id, UserId: th.BasicUser.Id, + Session: model.Session{ + Roles: "", + }, }, "@"+th.BasicUser2.Username+" hello") + channelName := model.GetDMNameFromIds(th.BasicUser.Id, th.BasicUser2.Id) + assert.Equal(t, "api.command_msg.permission.app_error", resp.Text) + assert.Equal(t, "", resp.GotoLocation) + + // Check with permission to create a DM channel. + resp = cmd.DoCommand(th.App, &model.CommandArgs{ + T: i18n.IdentityTfunc(), + SiteURL: "http://test.url", + TeamId: team.Id, + UserId: th.BasicUser.Id, + Session: model.Session{ + Roles: model.SYSTEM_USER_ROLE_ID, + }, + }, "@"+th.BasicUser2.Username+" hello") + + assert.Equal(t, "", resp.Text) + assert.Equal(t, "http://test.url/"+team.Name+"/channels/"+channelName, resp.GotoLocation) + + // Check without permission to post to an existing DM channel. + resp = cmd.DoCommand(th.App, &model.CommandArgs{ + T: i18n.IdentityTfunc(), + SiteURL: "http://test.url", + TeamId: team.Id, + UserId: th.BasicUser.Id, + Session: model.Session{ + Roles: "", + }, + }, "@"+th.BasicUser2.Username+" hello") + assert.Equal(t, "", resp.Text) assert.Equal(t, "http://test.url/"+team.Name+"/channels/"+channelName, resp.GotoLocation) } diff --git a/app/email.go b/app/email.go index b4e0a8983..569e6f454 100644 --- a/app/email.go +++ b/app/email.go @@ -10,12 +10,41 @@ import ( "net/http" "github.com/nicksnyder/go-i18n/i18n" + "github.com/pkg/errors" + "github.com/throttled/throttled" + "github.com/throttled/throttled/store/memstore" "github.com/mattermost/mattermost-server/mlog" "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/utils" ) +const ( + emailRateLimitingMemstoreSize = 65536 + emailRateLimitingPerHour = 20 + emailRateLimitingMaxBurst = 20 +) + +func (a *App) SetupInviteEmailRateLimiting() error { + store, err := memstore.New(emailRateLimitingMemstoreSize) + if err != nil { + return errors.Wrap(err, "Unable to setup email rate limiting memstore.") + } + + quota := throttled.RateQuota{ + MaxRate: throttled.PerHour(emailRateLimitingPerHour), + MaxBurst: emailRateLimitingMaxBurst, + } + + rateLimiter, err := throttled.NewGCRARateLimiter(store, quota) + if err != nil || rateLimiter == nil { + return errors.Wrap(err, "Unable to setup email rate limiting GCRA rate limiter.") + } + + a.EmailRateLimiter = rateLimiter + return nil +} + func (a *App) SendChangeUsernameEmail(oldUsername, newUsername, email, locale, siteURL string) *model.AppError { T := utils.GetUserTranslations(locale) @@ -247,7 +276,24 @@ func (a *App) SendMfaChangeEmail(email string, activated bool, locale, siteURL s return nil } -func (a *App) SendInviteEmails(team *model.Team, senderName string, invites []string, siteURL string) { +func (a *App) SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string) { + if a.EmailRateLimiter == nil { + a.Log.Error("Email invite not sent, rate limiting could not be setup.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id)) + return + } + rateLimited, result, err := a.EmailRateLimiter.RateLimit(senderUserId, len(invites)) + if rateLimited { + a.Log.Error("Invite emails rate limited.", + mlog.String("user_id", senderUserId), + mlog.String("team_id", team.Id), + mlog.String("retry_after", result.RetryAfter.String()), + mlog.Err(err)) + return + } else if err != nil { + a.Log.Error("Error rate limiting invite email.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id), mlog.Err(err)) + return + } + for _, invite := range invites { if len(invite) > 0 { senderRole := utils.T("api.team.invite_members.member") diff --git a/app/team.go b/app/team.go index beb4b1449..d9f19fab8 100644 --- a/app/team.go +++ b/app/team.go @@ -805,6 +805,10 @@ func (a *App) postRemoveFromTeamMessage(user *model.User, channel *model.Channel } func (a *App) InviteNewUsersToTeam(emailList []string, teamId, senderId string) *model.AppError { + if !*a.Config().ServiceSettings.EnableEmailInvitations { + return model.NewAppError("InviteNewUsersToTeam", "api.team.invite_members.disabled.app_error", nil, "", http.StatusNotImplemented) + } + if len(emailList) == 0 { err := model.NewAppError("InviteNewUsersToTeam", "api.team.invite_members.no_one.app_error", nil, "", http.StatusBadRequest) return err @@ -842,7 +846,7 @@ func (a *App) InviteNewUsersToTeam(emailList []string, teamId, senderId string) } nameFormat := *a.Config().TeamSettings.TeammateNameDisplay - a.SendInviteEmails(team, user.GetDisplayName(nameFormat), emailList, a.GetSiteURL()) + a.SendInviteEmails(team, user.GetDisplayName(nameFormat), user.Id, emailList, a.GetSiteURL()) return nil } |