diff --git a/e2e-tests/playwright/lib/src/server/default_config.ts b/e2e-tests/playwright/lib/src/server/default_config.ts index 9f63620d9fe..7101bf87b9d 100644 --- a/e2e-tests/playwright/lib/src/server/default_config.ts +++ b/e2e-tests/playwright/lib/src/server/default_config.ts @@ -804,7 +804,6 @@ const defaultServerConfig: AdminConfig = { WysiwygEditor: false, EnableExportDirectDownload: false, MoveThreadsEnabled: false, - CloudDedicatedExportUI: false, NotificationMonitoring: true, CustomProfileAttributes: true, AttributeValueMasking: false, diff --git a/server/channels/api4/system.go b/server/channels/api4/system.go index 4b96ba5f45c..15c26b055ab 100644 --- a/server/channels/api4/system.go +++ b/server/channels/api4/system.go @@ -591,12 +591,10 @@ func testFileStore(c *Context, w http.ResponseWriter, r *http.Request) { // Validate mandatory fields per driver. TestFileStoreConnectionWithConfig // will catch missing fields by failing to construct the backend, but a // dedicated validation step lets us surface a clearer error. - // - // When the dedicated export filestore is active the backend that is - // actually built and tested is the export backend, so we have to dispatch - // on ExportDriverName -- otherwise a primary=S3 / export=Azure deployment - // would run the S3 field check while testing the Azure backend. - driver := c.App.ResolvedFileStoreDriverName(&cfg.FileSettings) + driver := "" + if cfg.FileSettings.DriverName != nil { + driver = *cfg.FileSettings.DriverName + } switch driver { case model.ImageDriverLocal: // Local driver has no mandatory fields beyond the directory, which has a default. diff --git a/server/channels/api4/system_test.go b/server/channels/api4/system_test.go index c838b79575a..4bcd37cbf8b 100644 --- a/server/channels/api4/system_test.go +++ b/server/channels/api4/system_test.go @@ -826,87 +826,6 @@ func TestS3TestConnection(t *testing.T) { }) } -func TestFileStoreTestConnection(t *testing.T) { - mainHelper.Parallel(t) - th := SetupConfig(t, func(cfg *model.Config) { - cfg.FeatureFlags.CloudDedicatedExportUI = true - cfg.FileSettings.DedicatedExportStore = model.NewPointer(true) - }) - - th.App.Srv().SetLicense(model.NewTestLicense("cloud")) - t.Cleanup(func() { th.App.Srv().SetLicense(nil) }) - - baseSettings := func(primary, export string) model.FileSettings { - fs := model.FileSettings{} - fs.SetDefaults(false) - fs.DriverName = model.NewPointer(primary) - fs.ExportDriverName = model.NewPointer(export) - return fs - } - - t.Run("primary=local, export=azure: missing ExportAzureContainer surfaces azure error", func(t *testing.T) { - fs := baseSettings(model.ImageDriverLocal, model.ImageDriverAzure) - fs.ExportAzureStorageAccount = model.NewPointer("acmemattermost") - fs.ExportAzureAccessKey = model.NewPointer("secret") - fs.ExportAzureContainer = model.NewPointer("") - - resp, err := th.SystemAdminClient.TestS3Connection(context.Background(), &model.Config{FileSettings: fs}) - require.Error(t, err) - CheckErrorID(t, err, "api.admin.test_azure.missing_azure_field") - CheckBadRequestStatus(t, resp) - }) - - t.Run("primary=s3, export=azure: missing ExportAzureStorageAccount surfaces azure error", func(t *testing.T) { - fs := baseSettings(model.ImageDriverS3, model.ImageDriverAzure) - fs.ExportAzureStorageAccount = model.NewPointer("") - fs.ExportAzureContainer = model.NewPointer("mattermost") - fs.ExportAzureAccessKey = model.NewPointer("secret") - - resp, err := th.SystemAdminClient.TestS3Connection(context.Background(), &model.Config{FileSettings: fs}) - require.Error(t, err) - CheckErrorID(t, err, "api.admin.test_azure.missing_azure_field") - CheckBadRequestStatus(t, resp) - }) - - t.Run("primary=local, export=s3: missing ExportAmazonS3Bucket surfaces s3 error", func(t *testing.T) { - fs := baseSettings(model.ImageDriverLocal, model.ImageDriverS3) - fs.ExportAmazonS3Bucket = model.NewPointer("") - - resp, err := th.SystemAdminClient.TestS3Connection(context.Background(), &model.Config{FileSettings: fs}) - require.Error(t, err) - CheckErrorID(t, err, "api.admin.test_s3.missing_s3_bucket") - CheckBadRequestStatus(t, resp) - }) - - t.Run("unsupported export driver", func(t *testing.T) { - fs := baseSettings(model.ImageDriverS3, "bogus") - - resp, err := th.SystemAdminClient.TestS3Connection(context.Background(), &model.Config{FileSettings: fs}) - require.Error(t, err) - CheckErrorID(t, err, "api.file.test_connection_unsupported_driver.app_error") - CheckBadRequestStatus(t, resp) - }) - - t.Run("no license - primary dispatch", func(t *testing.T) { - // No license is set, so UseExportFileStore() must return false. Set an - // ExportDriverName that, if it were honored, would change the dispatch - // outcome -- this asserts that the primary path is taken. - appErr := th.App.Srv().RemoveLicense() - require.Nil(t, appErr) - fs := baseSettings(model.ImageDriverAzure, model.ImageDriverS3) - - // fs.AzureStorageAccount = model.NewPointer("") - // fs.AzureContainer = model.NewPointer("mattermost") - // fs.AzureAccessKey = model.NewPointer("secret") - // fs.ExportAmazonS3Bucket = model.NewPointer("export-bucket") - - resp, err := th.SystemAdminClient.TestS3Connection(context.Background(), &model.Config{FileSettings: fs}) - require.Error(t, err) - CheckErrorID(t, err, "api.admin.test_azure.missing_azure_field") - CheckBadRequestStatus(t, resp) - }) -} - func TestSupportedTimezones(t *testing.T) { mainHelper.Parallel(t) th := Setup(t) diff --git a/server/channels/app/file.go b/server/channels/app/file.go index 761b5d91543..0d19b456b0a 100644 --- a/server/channels/app/file.go +++ b/server/channels/app/file.go @@ -59,43 +59,8 @@ func (a *App) ExportFileBackend() filestore.FileBackend { return a.ch.exportFilestore } -// UseExportFileStore reports whether the dedicated export filestore is -// active. When true, callers reading the filestore configuration should -// resolve the export-side fields (ExportDriverName, ExportAmazonS3*, -// ExportAzure*, ...) rather than the primary fields. -func (a *App) UseExportFileStore() bool { - if !a.License().IsCloud() { - return false - } - if !a.Config().FeatureFlags.CloudDedicatedExportUI { - return false - } - dedicated := a.Config().FileSettings.DedicatedExportStore - return dedicated != nil && *dedicated -} - -// ResolvedFileStoreDriverName returns the driver name that callers should -// read for the active filestore -- ExportDriverName when the dedicated -// export filestore is enabled, otherwise the primary DriverName. The empty -// string is returned when neither pointer is set so callers can produce a -// dedicated "unsupported driver" error. -func (a *App) ResolvedFileStoreDriverName(settings *model.FileSettings) string { - name := settings.DriverName - if a.UseExportFileStore() { - name = settings.ExportDriverName - } - - if name == nil { - return "" - } - return *name -} - func (a *App) CheckMandatoryS3Fields(settings *model.FileSettings) *model.AppError { bucket := settings.AmazonS3Bucket - if a.UseExportFileStore() { - bucket = settings.ExportAmazonS3Bucket - } if bucket == nil || *bucket == "" { return model.NewAppError("CheckMandatoryS3Fields", "api.admin.test_s3.missing_s3_bucket", nil, "", http.StatusBadRequest) } @@ -107,12 +72,6 @@ func (a *App) CheckMandatoryAzureFields(settings *model.FileSettings) *model.App authMode := settings.AzureAuthMode accessKey := settings.AzureAccessKey container := settings.AzureContainer - if a.UseExportFileStore() { - storageAccount = settings.ExportAzureStorageAccount - authMode = settings.ExportAzureAuthMode - accessKey = settings.ExportAzureAccessKey - container = settings.ExportAzureContainer - } if storageAccount == nil || *storageAccount == "" { return model.NewAppError("CheckMandatoryAzureFields", "api.admin.test_azure.missing_azure_field", nil, "missing azure storage account setting", http.StatusBadRequest) } @@ -162,11 +121,7 @@ func (a *App) TestFileStoreConnectionWithConfig(cfg *model.FileSettings) *model. complianceEnabled := license != nil && *license.Features.Compliance allowInsecure := insecure != nil && *insecure allowedUntrustedInternalConnections := model.SafeDereference(a.Config().ServiceSettings.AllowedUntrustedInternalConnections) - if a.UseExportFileStore() { - backend, err = filestore.NewFileBackend(filestore.NewExportFileBackendSettingsFromConfig(cfg, complianceEnabled && license.IsCloud(), allowInsecure, allowedUntrustedInternalConnections)) - } else { - backend, err = filestore.NewFileBackend(filestore.NewFileBackendSettingsFromConfig(cfg, complianceEnabled, allowInsecure, allowedUntrustedInternalConnections)) - } + backend, err = filestore.NewFileBackend(filestore.NewFileBackendSettingsFromConfig(cfg, complianceEnabled, allowInsecure, allowedUntrustedInternalConnections)) if err != nil { return model.NewAppError("FileAttachmentBackend", "api.file.no_driver.app_error", nil, "", http.StatusInternalServerError).Wrap(err) } diff --git a/server/public/model/feature_flags.go b/server/public/model/feature_flags.go index f4da2123371..80bf57bfe03 100644 --- a/server/public/model/feature_flags.go +++ b/server/public/model/feature_flags.go @@ -34,8 +34,6 @@ type FeatureFlags struct { MoveThreadsEnabled bool - CloudDedicatedExportUI bool - NotificationMonitoring bool CustomProfileAttributes bool @@ -165,7 +163,6 @@ func (f *FeatureFlags) SetDefaults() { f.WysiwygEditor = false f.EnableExportDirectDownload = false f.MoveThreadsEnabled = false - f.CloudDedicatedExportUI = false f.NotificationMonitoring = true f.CustomProfileAttributes = true f.AttributeValueMasking = true diff --git a/webapp/channels/src/components/admin_console/admin_definition.tsx b/webapp/channels/src/components/admin_console/admin_definition.tsx index 1bf3a418954..ca4d74c0f8a 100644 --- a/webapp/channels/src/components/admin_console/admin_definition.tsx +++ b/webapp/channels/src/components/admin_console/admin_definition.tsx @@ -1592,352 +1592,6 @@ const AdminDefinition: AdminDefinitionType = { ], }, }, - export_storage: { - url: 'environment/export_storage', - title: defineMessage({id: 'admin.sidebar.exportStorage', defaultMessage: 'Export Storage'}), - isHidden: it.any( - it.not(it.licensedForFeature('Cloud')), - it.not(it.minLicenseTier(LicenseSkus.Enterprise)), - it.configIsFalse('FeatureFlags', 'CloudDedicatedExportUI'), - ), - schema: { - id: 'ExportFileSettings', - name: defineMessage({id: 'admin.sidebar.exportStorage', defaultMessage: 'Export Storage'}), - settings: [ - { - type: 'bool', - key: 'FileSettings.DedicatedExportStore', - label: defineMessage({id: 'admin.exportStorage.dedicatedExportStore', defaultMessage: 'Enable Dedicated Export Store:'}), - help_text: defineMessage({id: 'admin.exportStorage.dedicatedExportStoreDescription', defaultMessage: 'When enabled, Mattermost will use a dedicated export storage bucket for all export operations. This is required for Mattermost Cloud deployments.'}), - isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - }, - { - type: 'dropdown', - key: 'FileSettings.ExportDriverName', - label: defineMessage({id: 'admin.exportStorage.exportDriverName', defaultMessage: 'Export Storage Driver:'}), - isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - isHidden: it.stateEquals('FileSettings.DedicatedExportStore', false), - options: [ - { - value: FILE_STORAGE_DRIVER_S3, - display_name: defineMessage({id: 'admin.image.storeAmazonS3', defaultMessage: 'Amazon S3'}), - }, - { - value: FILE_STORAGE_DRIVER_AZURE, - display_name: defineMessage({id: 'admin.image.storeAzureBlob', defaultMessage: 'Azure Blob Storage'}), - }, - ], - }, - { - type: 'text', - key: 'FileSettings.ExportDirectory', - label: defineMessage({id: 'admin.exportStorage.exportDirectory', defaultMessage: 'Export Directory'}), - help_text: defineMessage({id: 'admin.image.exportDirectoryDescription', defaultMessage: 'Directory to which files are written. If blank, defaults to ./data/.'}), - placeholder: defineMessage({id: 'admin.image.localExample', defaultMessage: 'E.g.: "./data/"'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.stateEquals('FileSettings.DedicatedExportStore', false), - }, - { - type: 'text', - key: 'FileSettings.ExportAmazonS3AccessKeyId', - label: defineMessage({id: 'admin.image.amazonS3IdTitle', defaultMessage: 'Amazon S3 Access Key ID:'}), - help_text: defineMessage({id: 'admin.image.amazonS3IdDescription', defaultMessage: '(Optional) Only required if you do not want to authenticate to S3 using an IAM role. Enter the Access Key ID provided by your Amazon EC2 administrator.'}), // eslint-disable-line formatjs/enforce-placeholders -- link provided via help_text_values - help_text_values: { - link: (msg: string) => ( - - {msg} - - ), - }, - help_text_markdown: false, - placeholder: defineMessage({id: 'admin.image.amazonS3IdExample', defaultMessage: 'E.g.: "AKIADTOVBGERKLCBV"'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'text', - key: 'FileSettings.ExportAmazonS3SecretAccessKey', - label: defineMessage({id: 'admin.image.amazonS3SecretTitle', defaultMessage: 'Amazon S3 Secret Access Key:'}), - help_text: defineMessage({id: 'admin.image.amazonS3SecretDescription', defaultMessage: '(Optional) The secret access key associated with your Amazon S3 Access Key ID.'}), - placeholder: defineMessage({id: 'admin.image.amazonS3SecretExample', defaultMessage: 'E.g.: "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'text', - key: 'FileSettings.ExportAmazonS3Bucket', - label: defineMessage({id: 'admin.image.amazonS3BucketTitle', defaultMessage: 'Amazon S3 Bucket:'}), - help_text: defineMessage({id: 'admin.image.amazonS3BucketDescription', defaultMessage: 'Name you selected for your S3 bucket in AWS.'}), - placeholder: defineMessage({id: 'admin.image.amazonS3BucketExampleExport', defaultMessage: 'E.g.: "mattermost-export"'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'text', - key: 'FileSettings.ExportAmazonS3PathPrefix', - label: defineMessage({id: 'admin.image.amazonS3PathPrefixTitle', defaultMessage: 'Amazon S3 Path Prefix:'}), - help_text: defineMessage({id: 'admin.image.amazonS3PathPrefixDescription', defaultMessage: 'Prefix you selected for your S3 bucket in AWS.'}), - placeholder: defineMessage({id: 'admin.image.amazonS3PathPrefixExample', defaultMessage: 'E.g.: "subdir1" or you can leave it empty.'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'text', - key: 'FileSettings.ExportAmazonS3Region', - label: defineMessage({id: 'admin.image.amazonS3RegionTitle', defaultMessage: 'Amazon S3 Region:'}), - help_text: defineMessage({id: 'admin.image.amazonS3RegionDescription', defaultMessage: 'AWS region you selected when creating your S3 bucket. If no region is set, Mattermost attempts to get the appropriate region from AWS, or sets it to "us-east-1" if none found.'}), - placeholder: defineMessage({id: 'admin.image.amazonS3RegionExample', defaultMessage: 'E.g.: "us-east-1"'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'text', - key: 'FileSettings.ExportAmazonS3Endpoint', - label: defineMessage({id: 'admin.image.amazonS3EndpointTitle', defaultMessage: 'Amazon S3 Endpoint:'}), - help_text: defineMessage({id: 'admin.image.amazonS3EndpointDescription', defaultMessage: 'Hostname of your S3 Compatible Storage provider. Defaults to "s3.amazonaws.com".'}), - placeholder: defineMessage({id: 'admin.image.amazonS3EndpointExample', defaultMessage: 'E.g.: "s3.amazonaws.com"'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'bool', - key: 'FileSettings.ExportAmazonS3SSL', - label: defineMessage({id: 'admin.image.amazonS3SSLTitle', defaultMessage: 'Enable Secure Amazon S3 Connections:'}), - help_text: defineMessage({id: 'admin.image.amazonS3SSLDescription', defaultMessage: 'When false, allow insecure connections to Amazon S3. Defaults to secure connections only.'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'bool', - key: 'FileSettings.ExportAmazonSignV2', - label: defineMessage({id: 'admin.image.amazonS3SignV2', defaultMessage: 'Enable Sign V2'}), - help_text: defineMessage({id: 'admin.image.amazonS3SignV2Description', defaultMessage: 'When true, use Sign V2 for Amazon S3 connections'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'bool', - key: 'FileSettings.ExportAmazonS3SSE', - label: defineMessage({id: 'admin.image.amazonS3SSETitle', defaultMessage: 'Enable Server-Side Encryption for Amazon S3:'}), - help_text: defineMessage({id: 'admin.image.amazonS3SSEDescription', defaultMessage: 'When true, encrypt files in Amazon S3 using server-side encryption with Amazon S3-managed keys. See documentation to learn more.'}), // eslint-disable-line formatjs/enforce-placeholders -- link provided via help_text_values - help_text_values: { - link: (msg: string) => ( - - {msg} - - ), - }, - help_text_markdown: false, - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - }, - { - type: 'text', - key: 'FileSettings.ExportAmazonS3StorageClass', - label: defineMessage({id: 'admin.image.amazonS3StorageClassTitle', defaultMessage: 'Amazon S3 Storage Class:'}), - help_text: defineMessage({id: 'admin.image.amazonS3StorageClassDescription', defaultMessage: 'Storage class for your S3 Compatible Storage provider. Defaults to empty.'}), - placeholder: defineMessage({id: 'admin.image.amazonS3StorageClassExample', defaultMessage: 'E.g.: "STANDARD" or "STANDARD_IA"'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_S3)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'dropdown', - key: 'FileSettings.ExportAzureCloud', - label: defineMessage({id: 'admin.image.azureCloudTitle', defaultMessage: 'Azure Cloud:'}), - help_text: defineMessage({id: 'admin.image.azureCloudDescription', defaultMessage: 'The Azure cloud to connect to. Choose "Azure Commercial" or "Azure Government" to use the well-known endpoint for that cloud; only the storage account name is required. Choose "Custom Endpoint" to point at an arbitrary host such as Azurite, a reverse proxy, or any other Azure cloud (for example Azure China).'}), - options: [ - { - value: AZURE_CLOUD_COMMERCIAL, - display_name: defineMessage({id: 'admin.image.azureCloudCommercial', defaultMessage: 'Azure Commercial'}), - }, - { - value: AZURE_CLOUD_GOVERNMENT, - display_name: defineMessage({id: 'admin.image.azureCloudGovernment', defaultMessage: 'Azure Government'}), - }, - { - value: AZURE_CLOUD_CUSTOM, - display_name: defineMessage({id: 'admin.image.azureCloudCustom', defaultMessage: 'Custom Endpoint'}), - }, - ], - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'text', - key: 'FileSettings.ExportAzureStorageAccount', - label: defineMessage({id: 'admin.image.azureStorageAccountTitle', defaultMessage: 'Azure Storage Account:'}), - help_text: defineMessage({id: 'admin.image.azureStorageAccountDescription', defaultMessage: 'The name of your Azure Storage account.'}), - placeholder: defineMessage({id: 'admin.image.azureStorageAccountExample', defaultMessage: 'E.g.: "mattermoststorage"'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'text', - key: 'FileSettings.ExportAzureContainer', - label: defineMessage({id: 'admin.image.azureContainerTitle', defaultMessage: 'Azure Container:'}), - help_text: defineMessage({id: 'admin.image.azureContainerExportDescription', defaultMessage: 'Name of the container in your Azure Storage account.'}), - placeholder: defineMessage({id: 'admin.image.azureContainerExportExample', defaultMessage: 'E.g.: "mattermost-export"'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'text', - key: 'FileSettings.ExportAzurePathPrefix', - label: defineMessage({id: 'admin.image.azurePathPrefixTitle', defaultMessage: 'Azure Path Prefix:'}), - help_text: defineMessage({id: 'admin.image.azurePathPrefixDescription', defaultMessage: 'Optional path prefix to use for blobs in your Azure container.'}), - placeholder: defineMessage({id: 'admin.image.azurePathPrefixExample', defaultMessage: 'E.g.: "files/" or leave empty'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'dropdown', - key: 'FileSettings.ExportAzureAuthMode', - label: defineMessage({id: 'admin.image.azureAuthModeTitle', defaultMessage: 'Azure Authentication:'}), - help_text: defineMessage({id: 'admin.image.azureAuthModeDescription', defaultMessage: '"Shared key" signs requests with the Storage Account access key.\n \n"Default credential (Microsoft Entra ID)" reads the identity from the host environment - managed identity on Azure-hosted deployments, workload identity, service principal env vars, or "az login" for local development. No access key required.'}), // eslint-disable-line formatjs/no-multiple-whitespaces - help_text_markdown: true, - options: [ - { - value: AZURE_AUTH_MODE_SHARED_KEY, - display_name: defineMessage({id: 'admin.image.azureAuthModeSharedKey', defaultMessage: 'Shared key'}), - }, - { - value: AZURE_AUTH_MODE_DEFAULT_CREDENTIAL, - display_name: defineMessage({id: 'admin.image.azureAuthModeDefaultCredential', defaultMessage: 'Default credential (Microsoft Entra ID)'}), - }, - ], - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'text', - key: 'FileSettings.ExportAzureAccessKey', - label: defineMessage({id: 'admin.image.azureAccessKeyTitle', defaultMessage: 'Azure Storage Account Key:'}), - help_text: defineMessage({id: 'admin.image.azureAccessKeyDescription', defaultMessage: 'The shared key for your Azure Storage account.'}), - placeholder: defineMessage({id: 'admin.image.azureAccessKeyExample', defaultMessage: 'E.g.: "9MZbtYgfq18PJ8PbRaJ5u91IH8izHvReTbcuQzMl+So="'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - it.not(it.stateEquals('FileSettings.ExportAzureAuthMode', AZURE_AUTH_MODE_SHARED_KEY)), - ), - isHidden: it.any( - it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - it.not(it.stateEquals('FileSettings.ExportAzureAuthMode', AZURE_AUTH_MODE_SHARED_KEY)), - ), - }, - { - type: 'text', - key: 'FileSettings.ExportAzureEndpoint', - label: defineMessage({id: 'admin.image.azureEndpointTitle', defaultMessage: 'Custom Azure Endpoint:'}), - help_text: defineMessage({id: 'admin.image.azureEndpointDescription', defaultMessage: 'Full Blob service URL, including scheme and storage account. Mattermost does not modify this URL, so the storage account must already be embedded in the hostname (vhost-style, e.g. "https://acmemattermost.blob.core.chinacloudapi.cn/") or in the path (path-style, e.g. "http://localhost:10000/devstoreaccount1/"). Shared-key auth signs against the host this URL points at, so make sure it actually serves the storage account named above.'}), - placeholder: defineMessage({id: 'admin.image.azureEndpointExample', defaultMessage: 'E.g.: "http://localhost:10000/devstoreaccount1/"'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - it.not(it.stateEquals('FileSettings.ExportAzureCloud', AZURE_CLOUD_CUSTOM)), - ), - isHidden: it.any( - it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - it.not(it.stateEquals('FileSettings.ExportAzureCloud', AZURE_CLOUD_CUSTOM)), - ), - }, - { - type: 'bool', - key: 'FileSettings.ExportAzureSSL', - label: defineMessage({id: 'admin.image.azureSSLTitle', defaultMessage: 'Enable Secure Azure Blob Storage Connections:'}), - help_text: defineMessage({id: 'admin.image.azureSSLDescription', defaultMessage: 'When false, allow insecure connections to Azure Blob Storage. Defaults to secure connections only. Ignored for the Custom Endpoint cloud (the scheme is part of the endpoint URL).'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - it.stateEquals('FileSettings.ExportAzureCloud', AZURE_CLOUD_CUSTOM), - ), - isHidden: it.any( - it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - it.stateEquals('FileSettings.ExportAzureCloud', AZURE_CLOUD_CUSTOM), - ), - }, - { - type: 'number', - key: 'FileSettings.ExportAzureRequestTimeoutMilliseconds', - label: defineMessage({id: 'admin.image.azureRequestTimeoutTitle', defaultMessage: 'Azure Request Timeout (Milliseconds):'}), - help_text: defineMessage({id: 'admin.image.azureRequestTimeoutDescription', defaultMessage: 'Number of milliseconds to wait for a response from Azure Blob Storage before timing out.'}), - placeholder: defineMessage({id: 'admin.image.azureRequestTimeoutExample', defaultMessage: 'E.g.: "30000"'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - it.stateEquals('FileSettings.DedicatedExportStore', false), - ), - isHidden: it.any(it.not(it.stateEquals('FileSettings.ExportDriverName', FILE_STORAGE_DRIVER_AZURE)), it.stateEquals('FileSettings.DedicatedExportStore', false)), - }, - { - type: 'button', - action: testFileStoreConnection, - key: 'TestFileStoreConnection', - label: defineMessage({id: 'admin.filestore.connectionTest', defaultMessage: 'Test Connection'}), - loading: defineMessage({id: 'admin.filestore.testing', defaultMessage: 'Testing...'}), - error_message: defineMessage({id: 'admin.filestore.testFail', defaultMessage: 'Connection unsuccessful: {error}'}), // eslint-disable-line formatjs/enforce-placeholders -- error provided at runtime - success_message: defineMessage({id: 'admin.filestore.testSuccess', defaultMessage: 'Connection was successful'}), - isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.FILE_STORAGE)), - isHidden: it.stateEquals('FileSettings.DedicatedExportStore', false), - }, - ], - }, - }, image_proxy: { url: 'environment/image_proxy', title: defineMessage({id: 'admin.sidebar.imageProxy', defaultMessage: 'Image Proxy'}), diff --git a/webapp/channels/src/components/admin_console/admin_sidebar/__snapshots__/admin_sidebar.test.tsx.snap b/webapp/channels/src/components/admin_console/admin_sidebar/__snapshots__/admin_sidebar.test.tsx.snap index d8abafed4e3..a14b927b97b 100644 --- a/webapp/channels/src/components/admin_console/admin_sidebar/__snapshots__/admin_sidebar.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/admin_sidebar/__snapshots__/admin_sidebar.test.tsx.snap @@ -2814,25 +2814,6 @@ c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4z M20,4v5H4V4H20z M22,15v5c0,1.1-0.9,2-2,2H class="nav nav__sub-menu subsections" /> -