From 9a804a96fe293fea0c709bcb1569f2737785412b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Fri, 7 Oct 2022 15:57:44 +0200 Subject: [PATCH] =?UTF-8?q?MM-47228=20-=20restrict=20guest=20invitation=20?= =?UTF-8?q?flow=20if=20subscription=20plan=20does=20n=E2=80=A6=20(#21195)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * MM-47228 - restrict guest invitation flow if subscription plan does not support it * fix i18n texts and add unit test * test the scenario where guest invites are blocked by subscription * cover the success scenarios for cloud free trial and paid subscription * fix go vet * use the cloud prefix for the sku * fix unit tests * check the licence value to determine if the guest accounts are enabled * remove unnecessary changes for getting the subscription information * restrict user demotion if guestAccounts is not available in license Co-authored-by: Pablo Velez Vidal --- api4/team.go | 11 +++++++++-- api4/team_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ api4/user.go | 7 +++++++ api4/user_test.go | 20 ++++++++++++++++++++ i18n/en.json | 16 ++++++++-------- model/license.go | 19 +++++++++++++++++++ 6 files changed, 109 insertions(+), 10 deletions(-) diff --git a/api4/team.go b/api4/team.go index 5ee8a0d3da..6ad6c9b52b 100644 --- a/api4/team.go +++ b/api4/team.go @@ -1462,12 +1462,12 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) { func inviteGuestsToChannels(c *Context, w http.ResponseWriter, r *http.Request) { graceful := r.URL.Query().Get("graceful") != "" if c.App.Channels().License() == nil { - c.Err = model.NewAppError("Api4.InviteGuestsToChannels", "api.team.invate_guests_to_channels.license.error", nil, "", http.StatusNotImplemented) + c.Err = model.NewAppError("Api4.InviteGuestsToChannels", "api.team.invite_guests_to_channels.license.error", nil, "", http.StatusNotImplemented) return } if !*c.App.Config().GuestAccountsSettings.Enable { - c.Err = model.NewAppError("Api4.InviteGuestsToChannels", "api.team.invate_guests_to_channels.disabled.error", nil, "", http.StatusNotImplemented) + c.Err = model.NewAppError("Api4.InviteGuestsToChannels", "api.team.invite_guests_to_channels.disabled.error", nil, "", http.StatusNotImplemented) return } @@ -1485,6 +1485,13 @@ func inviteGuestsToChannels(c *Context, w http.ResponseWriter, r *http.Request) return } + guestEnabled := c.App.Channels().License() != nil && *c.App.Channels().License().Features.GuestAccounts + + if !guestEnabled { + c.Err = model.NewAppError("Api4.InviteGuestsToChannels", "api.team.invite_guests_to_channels.disabled.error", nil, "", http.StatusForbidden) + return + } + var guestsInvite model.GuestsInvite if err := json.NewDecoder(r.Body).Decode(&guestsInvite); err != nil { c.Err = model.NewAppError("Api4.inviteGuestsToChannels", "api.team.invite_guests_to_channels.invalid_body.app_error", nil, "", http.StatusBadRequest).Wrap(err) diff --git a/api4/team_test.go b/api4/team_test.go index a476f17903..04cca44f71 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -3361,6 +3361,52 @@ func TestInviteGuestsToTeam(t *testing.T) { }) } +func TestInviteGuest(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + guest1 := th.GenerateTestEmail() + guest2 := th.GenerateTestEmail() + + emailList := []string{guest1, guest2} + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableEmailInvitations = true }) + + t.Run("Guest Account not available in license returns forbidden", func(t *testing.T) { + th.App.Srv().SetLicense(model.NewTestLicenseWithFalseDefaults("guest_accounts")) + + guestsInvite := model.GuestsInvite{ + Emails: emailList, + Channels: []string{th.BasicChannel.Id}, + Message: "test message", + } + buf, err := json.Marshal(guestsInvite) + require.NoError(t, err) + + res, err := th.SystemAdminClient.DoAPIPost("/teams/"+th.BasicTeam.Id+"/invite-guests/email", string(buf)) + + require.Equal(t, http.StatusForbidden, res.StatusCode) + require.True(t, strings.Contains(err.Error(), "Guest accounts are disabled")) + require.Error(t, err) + }) + + t.Run("Guest Account available in license returns OK", func(t *testing.T) { + th.App.Srv().SetLicense(model.NewTestLicense("guest_accounts")) + + guestsInvite := model.GuestsInvite{ + Emails: emailList, + Channels: []string{th.BasicChannel.Id}, + Message: "test message", + } + buf, err := json.Marshal(guestsInvite) + require.NoError(t, err) + + res, err := th.SystemAdminClient.DoAPIPost("/teams/"+th.BasicTeam.Id+"/invite-guests/email", string(buf)) + + require.Equal(t, http.StatusOK, res.StatusCode) + require.NoError(t, err) + }) +} + func TestGetTeamInviteInfo(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() diff --git a/api4/user.go b/api4/user.go index 0cd58f56fb..423edc755c 100644 --- a/api4/user.go +++ b/api4/user.go @@ -2699,6 +2699,13 @@ func demoteUserToGuest(c *Context, w http.ResponseWriter, r *http.Request) { return } + guestEnabled := c.App.Channels().License() != nil && *c.App.Channels().License().Features.GuestAccounts + + if !guestEnabled { + c.Err = model.NewAppError("Api4.demoteUserToGuest", "api.team.invite_guests_to_channels.disabled.error", nil, "", http.StatusForbidden) + return + } + auditRec := c.MakeAuditRecord("demoteUserToGuest", audit.Fail) auditRec.AddEventParameter("user_id", c.Params.UserId) defer c.LogAuditRec(auditRec) diff --git a/api4/user_test.go b/api4/user_test.go index ba5eaa70a2..7f937fa7fc 100644 --- a/api4/user_test.go +++ b/api4/user_test.go @@ -5204,6 +5204,26 @@ func TestDemoteUserToGuest(t *testing.T) { th.App.Srv().SetLicense(model.NewTestLicense()) user := th.BasicUser + user2 := th.BasicUser2 + + t.Run("Guest Account not available in license returns forbidden", func(t *testing.T) { + th.App.Srv().SetLicense(model.NewTestLicenseWithFalseDefaults("guest_accounts")) + + res, err := th.SystemAdminClient.DoAPIPost("/users/"+user2.Id+"/demote", "") + + require.Equal(t, http.StatusForbidden, res.StatusCode) + require.True(t, strings.Contains(err.Error(), "Guest accounts are disabled")) + require.Error(t, err) + }) + + t.Run("Guest Account available in license returns OK", func(t *testing.T) { + th.App.Srv().SetLicense(model.NewTestLicense("guest_accounts")) + + res, err := th.SystemAdminClient.DoAPIPost("/users/"+user2.Id+"/demote", "") + + require.Equal(t, http.StatusOK, res.StatusCode) + require.NoError(t, err) + }) th.TestForSystemAdminAndLocal(t, func(t *testing.T, c *model.Client4) { _, _, err := c.GetUser(user.Id, "") diff --git a/i18n/en.json b/i18n/en.json index c2b52f87a3..539746b078 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -2975,22 +2975,22 @@ "id": "api.team.invalidate_all_email_invites.app_error", "translation": "Error invalidating email invites." }, - { - "id": "api.team.invate_guests_to_channels.disabled.error", - "translation": "Guest accounts are disabled" - }, - { - "id": "api.team.invate_guests_to_channels.license.error", - "translation": "Your license does not support guest accounts" - }, { "id": "api.team.invite_guests.channel_in_invalid_team.app_error", "translation": "The channels of the invite must be part of the team of the invite." }, + { + "id": "api.team.invite_guests_to_channels.disabled.error", + "translation": "Guest accounts are disabled" + }, { "id": "api.team.invite_guests_to_channels.invalid_body.app_error", "translation": "Invalid or missing request body." }, + { + "id": "api.team.invite_guests_to_channels.license.error", + "translation": "Your license does not support guest accounts" + }, { "id": "api.team.invite_members.disabled.app_error", "translation": "Email invitations are disabled." diff --git a/model/license.go b/model/license.go index 5fc844e7af..846ce68558 100644 --- a/model/license.go +++ b/model/license.go @@ -326,6 +326,25 @@ func NewTestLicense(features ...string) *License { return ret } +// NewTestLicense returns a license that expires in the future and set as false the given features. +func NewTestLicenseWithFalseDefaults(features ...string) *License { + ret := &License{ + ExpiresAt: GetMillis() + 90*DayInMilliseconds, + Customer: &Customer{}, + Features: &Features{}, + } + ret.Features.SetDefaults() + + featureMap := map[string]bool{} + for _, feature := range features { + featureMap[feature] = false + } + featureJson, _ := json.Marshal(featureMap) + json.Unmarshal(featureJson, &ret.Features) + + return ret +} + func NewTestLicenseSKU(skuShortName string, features ...string) *License { lic := NewTestLicense(features...) lic.SkuShortName = skuShortName