* Initial comit for ip filtering service implementation

* Add audit logs for IP Filters

* start of webapp work

* Stashing

* Updates based on Agniva's feedback around service vs einterface

* Updates completed

* Commit before refactoring, everything's working

* First pass of cleanup complete, front-end tests added

* actually add files

* Updates to some translation strings, running i18n-extract

* Lock everything behind a feature flag

* Fix tests, try to fix some linter stuff

* Fixed linter for JS, on to scss

* Fixed linter for scss

* Fix linter

* More fixes for pipeline

* Support for IPV6

* Remove tsx file that was removed in masteR

* Revert package.json and package-lock.json to master, add cidr-regex dep into channels/package.json

* Another commit to force fix Github

* Fixes around IPV6. Some suggestions from Matt re: UX review. Fixing pipelines for tests and types on new cidr-regex package

* Changes to address Matt's feedback

* A few more changes for clean up

* Add support for permissions

* Fix vet for OpenAPI spec

* Actually add the yaml file for openapi

* Add permission migration to allow support for IP Filtering

* Fix tests

* Final fixes from Matt

* Remove cancel button from page, update link outs to documentation

* Update test to account for removed cancel button

* Adjustments based on feedback from Harrison

* More fixes from PR feedback

* Add a t to fix translations that doesn't seem to be breaking anyone else?

* More fix

* updates for PR feedback

* Fix linter

* Fix types

* Now fix the linter again

* Add back tests because Harrison was able to get them running

* Adjustments for PR feedback

* Remove admin_definition.jsx

* Fix linter

* [CLD-6453] IP Filtering notification email for sysadmins (#25224)

* Initial commit for IP filtering alert email

* Updates to style for email, addition of ip_filtering email:

* Fix pipelines

* Adjustments from Matt's feedback

* Padding changes

* template diff (#25249)

Co-authored-by: Gabe Jackson <3694686+gabrieljackson@users.noreply.github.com>

* Fix hardcoded true, remove bool return value

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Gabe Jackson <3694686+gabrieljackson@users.noreply.github.com>

* Lock feature behind enterprise license. Drop cidr-regex in favour of ipaddr.js dependency. Refactor isIpAddressWithinRanges to use ipaddr.js

* Add a couple server tests

* fix linter

* Fix types from merge conflicts

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Gabe Jackson <3694686+gabrieljackson@users.noreply.github.com>
Этот коммит содержится в:
Nick Misasi
2023-11-14 09:12:04 -05:00
коммит произвёл GitHub
родитель 7bf9be2619
Коммит e1c851a3ca
82 изменённых файлов: 4533 добавлений и 19 удалений

Просмотреть файл

@@ -137,6 +137,8 @@ type Routes struct {
HostedCustomer *mux.Router // 'api/v4/hosted_customer'
Drafts *mux.Router // 'api/v4/drafts'
IPFiltering *mux.Router // 'api/v4/ip_filtering'
}
type API struct {
@@ -261,6 +263,8 @@ func Init(srv *app.Server) (*API, error) {
api.BaseRoutes.Drafts = api.BaseRoutes.APIRoot.PathPrefix("/drafts").Subrouter()
api.BaseRoutes.IPFiltering = api.BaseRoutes.APIRoot.PathPrefix("/ip_filtering").Subrouter()
api.InitUser()
api.InitBot()
api.InitTeam()
@@ -304,6 +308,7 @@ func Init(srv *app.Server) (*API, error) {
api.InitUsage()
api.InitHostedCustomer()
api.InitDrafts()
api.InitIPFiltering()
srv.Router.Handle("/api/v4/{anything:.*}", http.HandlerFunc(api.Handle404))

139
server/channels/api4/ip_filtering.go Обычный файл
Просмотреть файл

@@ -0,0 +1,139 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package api4
import (
"context"
"encoding/json"
"net/http"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/v8/channels/app"
"github.com/mattermost/mattermost/server/v8/channels/audit"
"github.com/mattermost/mattermost/server/v8/einterfaces"
)
func (api *API) InitIPFiltering() {
api.BaseRoutes.IPFiltering.Handle("", api.APISessionRequired(getIPFilters)).Methods("GET")
api.BaseRoutes.IPFiltering.Handle("", api.APISessionRequired(applyIPFilters)).Methods("POST")
api.BaseRoutes.IPFiltering.Handle("/my_ip", api.APISessionRequired(myIP)).Methods("GET")
}
func ensureIPFilteringInterface(c *Context, where string) (einterfaces.IPFilteringInterface, bool) {
if c.App.IPFiltering() == nil || !c.App.Config().FeatureFlags.CloudIPFiltering || c.App.License() == nil || c.App.License().SkuShortName != model.LicenseShortSkuEnterprise {
c.Err = model.NewAppError(where, "api.context.ip_filtering.not_available.app_error", nil, "", http.StatusNotImplemented)
return nil, false
}
return c.App.IPFiltering(), true
}
func getIPFilters(c *Context, w http.ResponseWriter, r *http.Request) {
ipFiltering, ok := ensureIPFilteringInterface(c, "getIPFilters")
if !ok {
return
}
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleReadIPFilters) {
c.SetPermissionError(model.PermissionSysconsoleReadIPFilters)
return
}
allowedRanges, err := ipFiltering.GetIPFilters()
if err != nil {
c.Err = model.NewAppError("getIPFilters", "api.context.ip_filtering.get_ip_filters.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
if err := json.NewEncoder(w).Encode(allowedRanges); err != nil {
c.Err = model.NewAppError("getIPFilters", "api.context.ip_filtering.get_ip_filters.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
}
func applyIPFilters(c *Context, w http.ResponseWriter, r *http.Request) {
ipFiltering, ok := ensureIPFilteringInterface(c, "applyIPFilters")
if !ok {
return
}
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleWriteIPFilters) {
c.SetPermissionError(model.PermissionSysconsoleWriteIPFilters)
return
}
auditRec := c.MakeAuditRecord("applyIPFilters", audit.Fail)
defer c.LogAuditRecWithLevel(auditRec, app.LevelContent)
allowedRanges := &model.AllowedIPRanges{} // Initialize the allowedRanges variable
if err := json.NewDecoder(r.Body).Decode(allowedRanges); err != nil {
c.Err = model.NewAppError("applyIPFilters", "api.context.ip_filtering.apply_ip_filters.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
audit.AddEventParameterAuditable(auditRec, "IPFilter", allowedRanges)
updatedAllowedRanges, err := ipFiltering.ApplyIPFilters(allowedRanges)
if err != nil {
c.Err = model.NewAppError("applyIPFilters", "api.context.ip_filtering.apply_ip_filters.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
auditRec.Success()
c.App.Srv().Go(func() {
initiatingUser, err := c.App.Srv().Store().User().GetProfileByIds(context.Background(), []string{c.AppContext.Session().UserId}, nil, true)
if err != nil {
mlog.Error("Failed to get initiating user", mlog.Err(err))
}
users, err := c.App.Srv().Store().User().GetSystemAdminProfiles()
if err != nil {
mlog.Error("Failed to get system admins", mlog.Err(err))
}
cloudWorkspaceOwnerEmailAddress := ""
if c.App.License().IsCloud() {
portalUserCustomer, cErr := c.App.Cloud().GetCloudCustomer(c.AppContext.Session().UserId)
if cErr != nil {
mlog.Error("Failed to get portal user customer", mlog.Err(cErr))
}
if cErr == nil && portalUserCustomer != nil {
cloudWorkspaceOwnerEmailAddress = portalUserCustomer.Email
}
}
for _, user := range users {
if err = c.App.Srv().EmailService.SendIPFiltersChangedEmail(user.Email, initiatingUser[0], *c.App.Config().ServiceSettings.SiteURL, *c.App.Config().CloudSettings.CWSURL, user.Locale, cloudWorkspaceOwnerEmailAddress == user.Email); err != nil {
mlog.Error("Error while sending IP filters changed email", mlog.Err(err))
}
}
})
if err := json.NewEncoder(w).Encode(updatedAllowedRanges); err != nil {
c.Err = model.NewAppError("getIPFilters", "api.context.ip_filtering.get_ip_filters.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
}
func myIP(c *Context, w http.ResponseWriter, r *http.Request) {
_, ok := ensureIPFilteringInterface(c, "myIP")
if !ok {
return
}
response := &model.GetIPAddressResponse{
IP: c.AppContext.IPAddress(),
}
json, err := json.Marshal(response)
if err != nil {
c.Err = model.NewAppError("myIP", "api.context.ip_filtering.get_my_ip.failed", nil, err.Error(), http.StatusInternalServerError)
return
}
w.Write(json)
}

310
server/channels/api4/ip_filtering_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,310 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package api4
import (
"context"
"os"
"testing"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/plugin/plugintest/mock"
"github.com/mattermost/mattermost/server/v8/einterfaces/mocks"
"github.com/stretchr/testify/require"
)
func Test_getIPFilters(t *testing.T) {
lic := &model.License{
Features: &model.Features{
CustomPermissionsSchemes: model.NewBool(false),
Cloud: model.NewBool(true),
},
Customer: &model.Customer{
Name: "TestName",
Email: "test@example.com",
},
SkuName: "SKU NAME",
SkuShortName: model.LicenseShortSkuEnterprise,
StartsAt: model.GetMillis() - 1000,
ExpiresAt: model.GetMillis() + 100000,
}
t.Run("No license returns 501", func(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_CLOUDIPFILTERING", "true")
defer os.Unsetenv("MM_FEATUREFLAGS_CLOUDIPFILTERING")
th := Setup(t).InitBasic()
defer th.TearDown()
ipFiltering := &mocks.IPFilteringInterface{}
ipFilteringImpl := th.App.Srv().IPFiltering
defer func() {
th.App.Srv().IPFiltering = ipFilteringImpl
}()
th.App.Srv().IPFiltering = ipFiltering
th.App.Srv().RemoveLicense()
th.Client.Login(context.Background(), th.BasicUser.Email, th.BasicUser.Password)
ipFilters, r, err := th.Client.GetIPFilters(context.Background())
require.Error(t, err)
require.Nil(t, ipFilters)
require.Equal(t, 501, r.StatusCode)
})
t.Run("No feature flag returns 501", func(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_CLOUDIPFILTERING", "false")
defer os.Unsetenv("MM_FEATUREFLAGS_CLOUDIPFILTERING")
th := Setup(t).InitBasic()
defer th.TearDown()
ipFiltering := &mocks.IPFilteringInterface{}
ipFilteringImpl := th.App.Srv().IPFiltering
defer func() {
th.App.Srv().IPFiltering = ipFilteringImpl
}()
th.App.Srv().IPFiltering = ipFiltering
th.App.Srv().SetLicense(lic)
th.Client.Login(context.Background(), th.BasicUser.Email, th.BasicUser.Password)
ipFilters, r, err := th.Client.GetIPFilters(context.Background())
require.Error(t, err)
require.Nil(t, ipFilters)
require.Equal(t, 501, r.StatusCode)
})
t.Run("Feature flag and license but no permission", func(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_CLOUDIPFILTERING", "true")
defer os.Unsetenv("MM_FEATUREFLAGS_CLOUDIPFILTERING")
th := Setup(t).InitBasic()
defer th.TearDown()
ipFiltering := &mocks.IPFilteringInterface{}
ipFilteringImpl := th.App.Srv().IPFiltering
defer func() {
th.App.Srv().IPFiltering = ipFilteringImpl
}()
th.App.Srv().IPFiltering = ipFiltering
th.App.Srv().SetLicense(lic)
th.Client.Login(context.Background(), th.BasicUser2.Email, th.BasicUser2.Password)
ipFilters, r, err := th.Client.GetIPFilters(context.Background())
require.Error(t, err)
require.Nil(t, ipFilters)
require.Equal(t, 403, r.StatusCode)
})
t.Run("Feature flag and license and permission", func(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_CLOUDIPFILTERING", "true")
defer os.Unsetenv("MM_FEATUREFLAGS_CLOUDIPFILTERING")
th := Setup(t).InitBasic()
defer th.TearDown()
ipFiltering := &mocks.IPFilteringInterface{}
ipFiltering.Mock.On("GetIPFilters").Return(&model.AllowedIPRanges{
model.AllowedIPRange{
CIDRBlock: "127.0.0.1/32",
Description: "test",
},
}, nil)
ipFilteringImpl := th.App.Srv().IPFiltering
defer func() {
th.App.Srv().IPFiltering = ipFilteringImpl
}()
th.App.Srv().IPFiltering = ipFiltering
th.App.Srv().SetLicense(lic)
th.Client.Login(context.Background(), th.SystemAdminUser.Email, th.SystemAdminUser.Password)
ipFilters, r, err := th.Client.GetIPFilters(context.Background())
require.NoError(t, err)
require.NotNil(t, ipFilters)
require.Equal(t, 200, r.StatusCode)
})
}
func Test_applyIPFilters(t *testing.T) {
allowedRanges := &model.AllowedIPRanges{
model.AllowedIPRange{
CIDRBlock: "127.0.0.1/32",
Description: "test",
},
}
lic := &model.License{
Features: &model.Features{
CustomPermissionsSchemes: model.NewBool(false),
Cloud: model.NewBool(true),
},
Customer: &model.Customer{
Name: "TestName",
Email: "test@example.com",
},
SkuName: "SKU NAME",
SkuShortName: model.LicenseShortSkuEnterprise,
StartsAt: model.GetMillis() - 1000,
ExpiresAt: model.GetMillis() + 100000,
}
// Initialize the allowedRanges variable
t.Run("No license returns 501", func(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_CLOUDIPFILTERING", "true")
defer os.Unsetenv("MM_FEATUREFLAGS_CLOUDIPFILTERING")
th := Setup(t).InitBasic()
defer th.TearDown()
ipFiltering := &mocks.IPFilteringInterface{}
ipFilteringImpl := th.App.Srv().IPFiltering
defer func() {
th.App.Srv().IPFiltering = ipFilteringImpl
}()
th.App.Srv().IPFiltering = ipFiltering
th.App.Srv().RemoveLicense()
th.Client.Login(context.Background(), th.BasicUser.Email, th.BasicUser.Password)
ipFilters, r, err := th.Client.ApplyIPFilters(context.Background(), allowedRanges)
require.Error(t, err)
require.Nil(t, ipFilters)
require.Equal(t, 501, r.StatusCode)
})
t.Run("License but no feature flag returns 501", func(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_CLOUDIPFILTERING", "false")
defer os.Unsetenv("MM_FEATUREFLAGS_CLOUDIPFILTERING")
th := Setup(t).InitBasic()
defer th.TearDown()
ipFiltering := &mocks.IPFilteringInterface{}
ipFilteringImpl := th.App.Srv().IPFiltering
defer func() {
th.App.Srv().IPFiltering = ipFilteringImpl
}()
th.App.Srv().IPFiltering = ipFiltering
th.App.Srv().SetLicense(lic)
th.Client.Login(context.Background(), th.BasicUser.Email, th.BasicUser.Password)
ipFilters, r, err := th.Client.ApplyIPFilters(context.Background(), allowedRanges)
require.Error(t, err)
require.Nil(t, ipFilters)
require.Equal(t, 501, r.StatusCode)
})
t.Run("feature flag and license but no permission", func(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_CLOUDIPFILTERING", "true")
defer os.Unsetenv("MM_FEATUREFLAGS_CLOUDIPFILTERING")
th := Setup(t).InitBasic()
defer th.TearDown()
th.Client.Login(context.Background(), th.BasicUser.Email, th.BasicUser.Password)
ipFiltering := &mocks.IPFilteringInterface{}
ipFilteringImpl := th.App.Srv().IPFiltering
defer func() {
th.App.Srv().IPFiltering = ipFilteringImpl
}()
th.App.Srv().IPFiltering = ipFiltering
th.App.Srv().SetLicense(lic)
ipFilters, r, err := th.Client.ApplyIPFilters(context.Background(), allowedRanges)
require.Error(t, err)
require.Nil(t, ipFilters)
require.Equal(t, 403, r.StatusCode)
})
t.Run("Feature flag and license and permission", func(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_CLOUDIPFILTERING", "true")
defer os.Unsetenv("MM_FEATUREFLAGS_CLOUDIPFILTERING")
th := Setup(t).InitBasic()
defer th.TearDown()
ipFiltering := &mocks.IPFilteringInterface{}
ipFiltering.Mock.On("ApplyIPFilters", mock.Anything).Return(&model.AllowedIPRanges{
model.AllowedIPRange{
CIDRBlock: "127.0.0.1/32",
Description: "test",
},
}, nil)
ipFilteringImpl := th.App.Srv().IPFiltering
defer func() {
th.App.Srv().IPFiltering = ipFilteringImpl
}()
th.App.Srv().IPFiltering = ipFiltering
th.App.Srv().SetLicense(lic)
th.Client.Login(context.Background(), th.SystemAdminUser.Email, th.SystemAdminUser.Password)
ipFilters, r, err := th.Client.ApplyIPFilters(context.Background(), allowedRanges)
require.NoError(t, err)
require.NotNil(t, ipFilters)
require.Equal(t, 200, r.StatusCode)
})
}
func Test_getMyIP(t *testing.T) {
lic := &model.License{
Features: &model.Features{
CustomPermissionsSchemes: model.NewBool(false),
Cloud: model.NewBool(true),
},
Customer: &model.Customer{
Name: "TestName",
Email: "test@example.com",
},
SkuName: "SKU NAME",
SkuShortName: model.LicenseShortSkuEnterprise,
StartsAt: model.GetMillis() - 1000,
ExpiresAt: model.GetMillis() + 100000,
}
t.Run("No license returns 501", func(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_CLOUDIPFILTERING", "true")
defer os.Unsetenv("MM_FEATUREFLAGS_CLOUDIPFILTERING")
th := Setup(t).InitBasic()
defer th.TearDown()
ipFiltering := &mocks.IPFilteringInterface{}
ipFilteringImpl := th.App.Srv().IPFiltering
defer func() {
th.App.Srv().IPFiltering = ipFilteringImpl
}()
th.App.Srv().IPFiltering = ipFiltering
th.App.Srv().RemoveLicense()
th.Client.Login(context.Background(), th.BasicUser.Email, th.BasicUser.Password)
myIP, r, err := th.Client.GetMyIP(context.Background())
require.Error(t, err)
require.Nil(t, myIP)
require.Equal(t, 501, r.StatusCode)
})
t.Run("Licensed, but no feature flag returns 501", func(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_CLOUDIPFILTERING", "false")
defer os.Unsetenv("MM_FEATUREFLAGS_CLOUDIPFILTERING")
th := Setup(t).InitBasic()
defer th.TearDown()
th.Client.Login(context.Background(), th.BasicUser.Email, th.BasicUser.Password)
ipFiltering := &mocks.IPFilteringInterface{}
ipFilteringImpl := th.App.Srv().IPFiltering
defer func() {
th.App.Srv().IPFiltering = ipFilteringImpl
}()
th.App.Srv().IPFiltering = ipFiltering
th.App.Srv().SetLicense(lic)
myIP, r, err := th.Client.GetMyIP(context.Background())
require.Error(t, err)
require.Nil(t, myIP)
require.Equal(t, 501, r.StatusCode)
})
}

Просмотреть файл

@@ -103,6 +103,11 @@ func (a *App) Saml() einterfaces.SamlInterface {
func (a *App) Cloud() einterfaces.CloudInterface {
return a.ch.srv.Cloud
}
func (a *App) IPFiltering() einterfaces.IPFilteringInterface {
return a.ch.srv.IPFiltering
}
func (a *App) HTTPService() httpservice.HTTPService {
return a.ch.srv.httpService
}

Просмотреть файл

@@ -868,6 +868,7 @@ type AppIface interface {
HasPermissionToTeam(c request.CTX, askingUserId string, teamID string, permission *model.Permission) bool
HasPermissionToUser(askingUserId string, userID string) bool
HasSharedChannel(channelID string) (bool, error)
IPFiltering() einterfaces.IPFilteringInterface
ImageProxy() *imageproxy.ImageProxy
ImageProxyAdder() func(string) string
ImageProxyRemover() (f func(string) string)

Просмотреть файл

@@ -1275,3 +1275,40 @@ func (es *Service) SendRemoveExpiredLicenseEmail(ctaText, ctaLink, email, locale
return nil
}
func (es *Service) SendIPFiltersChangedEmail(email string, initiatingUser *model.User, siteURL, portalURL, locale string, isWorkspaceOwner bool) error {
T := i18n.GetUserTranslations(locale)
subject := T("api.templates.ip_filters_changed.subject")
data := es.NewEmailTemplateData(locale)
data.Props["SiteURL"] = siteURL
data.Props["Title"] = T("api.templates.ip_filters_changed.title")
data.Props["SubTitle"] = T("api.templates.ip_filters_changed.subTitle", map[string]any{"InitiatingUsername": initiatingUser.Username, "SiteURL": siteURL})
data.Props["ButtonURL"] = siteURL + "/admin_console/site_config/ip_filtering"
data.Props["Button"] = T("api.templates.ip_filters_changed.button")
data.Props["TroubleAccessingTitle"] = T("api.templates.ip_filters_changed_footer.title")
data.Props["SendAnEmailTo"] = T("api.templates.ip_filters_changed_footer.send_an_email_to", map[string]any{"InitiatingUserEmail": initiatingUser.Email})
data.Props["PortalURL"] = portalURL
// If the email we're sending to was the one who initiated the change, we don't want to show their email address as a mailto
if email != initiatingUser.Email {
data.Props["ActorEmail"] = initiatingUser.Email
}
if isWorkspaceOwner {
data.Props["LogInToCustomerPortal"] = T("api.templates.ip_filters_changed_footer.log_in_to_customer_portal")
}
data.Props["ContactSupport"] = T("api.templates.ip_filters_changed_footer.contact_support")
data.Props["SupportEmail"] = *es.config().SupportSettings.SupportEmail
body, err := es.templatesContainer.RenderToString("ip_filters_changed", data)
if err != nil {
return err
}
if err := es.sendMail(email, subject, body, "PasswordResetEmail"); err != nil {
return err
}
return nil
}

Просмотреть файл

@@ -310,6 +310,20 @@ func (_m *ServiceInterface) SendGuestInviteEmails(team *model.Team, channels []*
return r0
}
// SendIPFiltersChangedEmail provides a mock function with given fields: _a0, userWhoChangedFilter, siteURL, portalURL, locale, isWorkspaceOwner
func (_m *ServiceInterface) SendIPFiltersChangedEmail(_a0 string, userWhoChangedFilter *model.User, siteURL string, portalURL string, locale string, isWorkspaceOwner bool) error {
ret := _m.Called(_a0, userWhoChangedFilter, siteURL, portalURL, locale, isWorkspaceOwner)
var r0 error
if rf, ok := ret.Get(0).(func(string, *model.User, string, string, string, bool) error); ok {
r0 = rf(_a0, userWhoChangedFilter, siteURL, portalURL, locale, isWorkspaceOwner)
} else {
r0 = ret.Error(0)
}
return r0
}
// SendInviteEmails provides a mock function with given fields: team, senderName, senderUserId, invites, siteURL, reminderData, errorWhenNotSent, isSystemAdmin, isFirstAdmin
func (_m *ServiceInterface) SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, errorWhenNotSent bool, isSystemAdmin bool, isFirstAdmin bool) error {
ret := _m.Called(team, senderName, senderUserId, invites, siteURL, reminderData, errorWhenNotSent, isSystemAdmin, isFirstAdmin)

Просмотреть файл

@@ -163,6 +163,7 @@ type ServiceInterface interface {
InitEmailBatching()
SendChangeUsernameEmail(newUsername, email, locale, siteURL string) error
CreateVerifyEmailToken(userID string, newEmail string) (*model.Token, error)
SendIPFiltersChangedEmail(email string, userWhoChangedFilter *model.User, siteURL, portalURL, locale string, isWorkspaceOwner bool) error
Stop()
}

Просмотреть файл

@@ -92,6 +92,12 @@ func RegisterNotificationInterface(f func(*App) einterfaces.NotificationInterfac
notificationInterface = f
}
var ipFilteringInterface func(*App) einterfaces.IPFilteringInterface
func RegisterIPFilteringInterface(f func(*App) einterfaces.IPFilteringInterface) {
ipFilteringInterface = f
}
func (s *Server) initEnterprise() {
if cloudInterface != nil {
s.Cloud = cloudInterface(s)

Просмотреть файл

@@ -11560,6 +11560,23 @@ func (a *OpenTracingAppLayer) HubUnregister(webConn *platform.WebConn) {
a.app.HubUnregister(webConn)
}
func (a *OpenTracingAppLayer) IPFiltering() einterfaces.IPFilteringInterface {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.IPFiltering")
a.ctx = newCtx
a.app.Srv().Store().SetContext(newCtx)
defer func() {
a.app.Srv().Store().SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.IPFiltering()
return resultVar0
}
func (a *OpenTracingAppLayer) ImageProxyAdder() func(string) string {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ImageProxyAdder")

Просмотреть файл

@@ -1117,6 +1117,30 @@ func (a *App) getAddChannelReadContentPermissions() (permissionsMap, error) {
return t, nil
}
func (a *App) getAddIPFilterPermissionsMigration() (permissionsMap, error) {
t := []permissionTransformation{}
ipFilterPermissionsRead := []string{
model.PermissionSysconsoleReadIPFilters.Id,
}
ipFilterPermissionsWrite := []string{
model.PermissionSysconsoleWriteIPFilters.Id,
}
t = append(t, permissionTransformation{
On: permissionOr(isExactRole(model.SystemAdminRoleId)),
Add: ipFilterPermissionsRead,
})
t = append(t, permissionTransformation{
On: permissionOr(isExactRole(model.SystemAdminRoleId)),
Add: ipFilterPermissionsWrite,
})
return t, nil
}
// DoPermissionsMigrations execute all the permissions migrations need by the current version.
func (a *App) DoPermissionsMigrations() error {
return a.Srv().doPermissionsMigrations()
@@ -1161,6 +1185,7 @@ func (s *Server) doPermissionsMigrations() error {
{Key: model.MigrationKeyAddProductsBoardsPermissions, Migration: a.getProductsBoardsPermissions},
{Key: model.MigrationKeyAddCustomUserGroupsPermissionRestore, Migration: a.getAddCustomUserGroupsPermissionRestore},
{Key: model.MigrationKeyAddReadChannelContentPermissions, Migration: a.getAddChannelReadContentPermissions},
{Key: model.MigrationKeyAddIPFilteringPermissions, Migration: a.getAddIPFilterPermissionsMigration},
}
roles, err := s.Store().Role().GetAll()

Просмотреть файл

@@ -141,7 +141,8 @@ type Server struct {
// startSearchEngine bool
skipPostInit bool
Cloud einterfaces.CloudInterface
Cloud einterfaces.CloudInterface
IPFiltering einterfaces.IPFilteringInterface
tracer *tracing.Tracer
@@ -396,6 +397,10 @@ func NewServer(options ...Option) (*Server, error) {
s.initJobs()
if ipFilteringInterface != nil {
s.IPFiltering = ipFilteringInterface(app)
}
s.clusterLeaderListenerId = s.AddClusterLeaderChangedListener(func() {
mlog.Info("Cluster leader changed. Determining if job schedulers should be running:", mlog.Bool("isLeader", s.IsLeader()))
if s.Jobs != nil {

Просмотреть файл

@@ -72,6 +72,7 @@ func GetMockStoreForSetupFunctions() *mocks.Store {
systemStore.On("GetByName", model.MigrationKeyAddCustomUserGroupsPermissionRestore).Return(&model.System{Name: model.MigrationKeyAddCustomUserGroupsPermissionRestore, Value: "true"}, nil)
systemStore.On("GetByName", model.MigrationKeyAddReadChannelContentPermissions).Return(&model.System{Name: model.MigrationKeyAddReadChannelContentPermissions, Value: "true"}, nil)
systemStore.On("GetByName", model.MigrationKeyDeleteEmptyDrafts).Return(&model.System{Name: model.MigrationKeyDeleteEmptyDrafts, Value: "true"}, nil)
systemStore.On("GetByName", model.MigrationKeyAddIPFilteringPermissions).Return(&model.System{Name: model.MigrationKeyAddIPFilteringPermissions, Value: "true"}, nil)
systemStore.On("GetByName", "CustomGroupAdminRoleCreationMigrationComplete").Return(&model.System{Name: model.MigrationKeyAddPlayboosksManageRolesPermissions, Value: "true"}, nil)
systemStore.On("GetByName", "products_boards").Return(&model.System{Name: "products_boards", Value: "true"}, nil)
systemStore.On("GetByName", "elasticsearch_fix_channel_index_migration").Return(&model.System{Name: "elasticsearch_fix_channel_index_migration", Value: "true"}, nil)