From 54939d47c0b64f0c15c0c992223f7db1aff2476a Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Tue, 18 Aug 2026 07:41:05 -0300 Subject: [PATCH 1/5] [MM-68249] Drop support for OpenSearch v1.x (#37283) --- .../elasticsearch/opensearch/check_version_test.go | 8 ++++++++ server/enterprise/elasticsearch/opensearch/opensearch.go | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/server/enterprise/elasticsearch/opensearch/check_version_test.go b/server/enterprise/elasticsearch/opensearch/check_version_test.go index 90184ce215b..9d4733e6097 100644 --- a/server/enterprise/elasticsearch/opensearch/check_version_test.go +++ b/server/enterprise/elasticsearch/opensearch/check_version_test.go @@ -61,6 +61,13 @@ func TestCheckVersion(t *testing.T) { wantVersion: "3.0.0", wantMajor: 3, }, + { + name: "OpenSearch 1 is too old, but allowed", + version: "1.3.0", + wantVersion: "1.3.0", + wantMajor: 1, + wantUnsupported: true, + }, { name: "OpenSearch 4 is too new, but allowed", version: "4.0.0", @@ -97,6 +104,7 @@ func TestCheckVersion(t *testing.T) { if tc.wantUnsupported { assert.Contains(t, buf.String(), "Unsupported OpenSearch version") assert.Contains(t, buf.String(), fmt.Sprintf(`"version":%q`, tc.wantVersion)) + assert.Contains(t, buf.String(), `"min_version":2`) assert.Contains(t, buf.String(), `"max_version":3`) } else { assert.Empty(t, buf.String()) diff --git a/server/enterprise/elasticsearch/opensearch/opensearch.go b/server/enterprise/elasticsearch/opensearch/opensearch.go index f43682f5ca2..4ebd8b3c71b 100644 --- a/server/enterprise/elasticsearch/opensearch/opensearch.go +++ b/server/enterprise/elasticsearch/opensearch/opensearch.go @@ -35,6 +35,7 @@ import ( "github.com/mattermost/mattermost/server/v8/platform/services/searchengine" ) +const opensearchMinVersion = 2 const opensearchMaxVersion = 3 var ( @@ -2369,9 +2370,10 @@ func checkVersion(ctx context.Context, client *opensearchapi.Client, logger mlog return "", 0, model.NewAppError("Opensearch.checkVersion", "ent.elasticsearch.start.parse_server_version.app_error", map[string]any{"Backend": model.ElasticsearchSettingsOSBackend}, "", http.StatusInternalServerError).Wrap(esErr) } - if major > opensearchMaxVersion { + if major < opensearchMinVersion || major > opensearchMaxVersion { logger.Error("Unsupported OpenSearch version. Running an unsupported version may lead to unexpected behaviour.", mlog.String("version", resp.Version.Number), + mlog.Int("min_version", opensearchMinVersion), mlog.Int("max_version", opensearchMaxVersion), ) } From 95fc4743df54efd8bbaba148c0e8542d8158c7c1 Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Tue, 18 Aug 2026 07:59:32 -0300 Subject: [PATCH 2/5] Drop RHEL 7/8 support: switch build image to golang-bookworm (#37229) --- README.md | 2 -- server/build/Dockerfile.buildenv | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index d837b41a628..448f0f269a0 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,6 @@ Other install guides: - [Ubuntu 20.04 LTS](https://docs.mattermost.com/install/installing-ubuntu-2004-LTS.html) - [Kubernetes](https://docs.mattermost.com/install/install-kubernetes.html) - [Helm](https://docs.mattermost.com/install/install-kubernetes.html#installing-the-operators-via-helm) -- [Debian Buster](https://docs.mattermost.com/install/install-debian.html) -- [RHEL 8](https://docs.mattermost.com/install/install-rhel-8.html) - [More server install guides](https://docs.mattermost.com/guides/deployment.html) ## Native mobile and desktop apps diff --git a/server/build/Dockerfile.buildenv b/server/build/Dockerfile.buildenv index d19c5ff3e76..75fe3d322ee 100644 --- a/server/build/Dockerfile.buildenv +++ b/server/build/Dockerfile.buildenv @@ -1,4 +1,4 @@ -FROM mattermost/golang-bullseye:1.26.4@sha256:6c979d9bf17e17dcd52fa052867f2910a4b9bca3f10d4c439e0b8f233a53d426 +FROM golang:1.26.4-bookworm@sha256:b305420a68d0f229d91eb3b3ed9e519fcf2cf5461da4bef997bf927e8c0bfd2b ARG NODE_VERSION=20.11.1 RUN apt-get update && apt-get install -y make git apt-transport-https ca-certificates curl software-properties-common build-essential zip xmlsec1 jq pgloader gnupg From dc6ab54f82f624ec2bc1ffc895484df8198a063c Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Tue, 18 Aug 2026 08:03:05 -0300 Subject: [PATCH 3/5] MM-67510 Drop deprecated autotranslation column from ChannelMembers (#37496) --- server/channels/db/migrations/migrations.list | 4 ++++ .../000214_drop_channelmembers_autotranslation.down.sql | 8 ++++++++ .../000214_drop_channelmembers_autotranslation.up.sql | 5 +++++ ...15_drop_channelmembers_autotranslation_column.down.sql | 5 +++++ ...0215_drop_channelmembers_autotranslation_column.up.sql | 5 +++++ 5 files changed, 27 insertions(+) create mode 100644 server/channels/db/migrations/postgres/000214_drop_channelmembers_autotranslation.down.sql create mode 100644 server/channels/db/migrations/postgres/000214_drop_channelmembers_autotranslation.up.sql create mode 100644 server/channels/db/migrations/postgres/000215_drop_channelmembers_autotranslation_column.down.sql create mode 100644 server/channels/db/migrations/postgres/000215_drop_channelmembers_autotranslation_column.up.sql diff --git a/server/channels/db/migrations/migrations.list b/server/channels/db/migrations/migrations.list index 3ec0c0d59ae..06ca4ab90d3 100644 --- a/server/channels/db/migrations/migrations.list +++ b/server/channels/db/migrations/migrations.list @@ -421,3 +421,7 @@ channels/db/migrations/postgres/000212_add_scheduled_post_recurrence.down.sql channels/db/migrations/postgres/000212_add_scheduled_post_recurrence.up.sql channels/db/migrations/postgres/000213_add_scheduled_post_pending_index.down.sql channels/db/migrations/postgres/000213_add_scheduled_post_pending_index.up.sql +channels/db/migrations/postgres/000214_drop_channelmembers_autotranslation.down.sql +channels/db/migrations/postgres/000214_drop_channelmembers_autotranslation.up.sql +channels/db/migrations/postgres/000215_drop_channelmembers_autotranslation_column.down.sql +channels/db/migrations/postgres/000215_drop_channelmembers_autotranslation_column.up.sql diff --git a/server/channels/db/migrations/postgres/000214_drop_channelmembers_autotranslation.down.sql b/server/channels/db/migrations/postgres/000214_drop_channelmembers_autotranslation.down.sql new file mode 100644 index 00000000000..9fc4a9381b3 --- /dev/null +++ b/server/channels/db/migrations/postgres/000214_drop_channelmembers_autotranslation.down.sql @@ -0,0 +1,8 @@ +-- morph:nontransactional +-- Recreate the partial index on the channelmembers autotranslation column, +-- mirroring migration 000147. Runs after 000215 restores the column. +-- CONCURRENTLY cannot run inside a transaction, so this must be the only +-- statement in the file. +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_channelmembers_autotranslation_enabled + ON channelmembers (channelid) + WHERE autotranslation = true; diff --git a/server/channels/db/migrations/postgres/000214_drop_channelmembers_autotranslation.up.sql b/server/channels/db/migrations/postgres/000214_drop_channelmembers_autotranslation.up.sql new file mode 100644 index 00000000000..e191d38f024 --- /dev/null +++ b/server/channels/db/migrations/postgres/000214_drop_channelmembers_autotranslation.up.sql @@ -0,0 +1,5 @@ +-- morph:nontransactional +-- Drop the partial index on the deprecated channelmembers autotranslation +-- column ahead of dropping the column itself in 000215. CONCURRENTLY cannot run +-- inside a transaction, so this must be the only statement in the file. +DROP INDEX CONCURRENTLY IF EXISTS idx_channelmembers_autotranslation_enabled; diff --git a/server/channels/db/migrations/postgres/000215_drop_channelmembers_autotranslation_column.down.sql b/server/channels/db/migrations/postgres/000215_drop_channelmembers_autotranslation_column.down.sql new file mode 100644 index 00000000000..342a3873f3c --- /dev/null +++ b/server/channels/db/migrations/postgres/000215_drop_channelmembers_autotranslation_column.down.sql @@ -0,0 +1,5 @@ +-- Recreate the deprecated channelmembers autotranslation column, mirroring +-- migration 000147. Prior values are not restorable. The partial index is +-- recreated separately in 000214. +ALTER TABLE channelmembers + ADD COLUMN IF NOT EXISTS autotranslation boolean NOT NULL DEFAULT false; diff --git a/server/channels/db/migrations/postgres/000215_drop_channelmembers_autotranslation_column.up.sql b/server/channels/db/migrations/postgres/000215_drop_channelmembers_autotranslation_column.up.sql new file mode 100644 index 00000000000..3cfa7121eb4 --- /dev/null +++ b/server/channels/db/migrations/postgres/000215_drop_channelmembers_autotranslation_column.up.sql @@ -0,0 +1,5 @@ +-- Drop the deprecated channelmembers autotranslation column (opt-in semantics), +-- replaced by autotranslationdisabled (opt-out) in migration 000151. The partial +-- index on this column is dropped separately in 000214. +ALTER TABLE channelmembers + DROP COLUMN IF EXISTS autotranslation; From 0912a75c9c4580a00f14c565215fed5ffcb0fbf7 Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Tue, 18 Aug 2026 08:23:20 -0300 Subject: [PATCH 4/5] Bump minimum supported Postgres version to v15 (#37285) --- .github/workflows/server-ci.yml | 2 +- e2e-tests/.ci/server.generate.sh | 2 +- server/build/docker-compose.common.yml | 2 +- server/channels/store/sqlstore/store.go | 2 +- server/channels/store/sqlstore/store_test.go | 19 ++++++++++++++----- server/docker-compose.makefile.yml | 4 ++-- server/docker-compose.pgvector.yml | 2 +- 7 files changed, 21 insertions(+), 12 deletions(-) diff --git a/.github/workflows/server-ci.yml b/.github/workflows/server-ci.yml index 5be13f02388..0b1ca9b112d 100644 --- a/.github/workflows/server-ci.yml +++ b/.github/workflows/server-ci.yml @@ -82,7 +82,7 @@ jobs: services: # make default-roles-permissions snapshots a live database. postgres: - image: postgres:14 + image: postgres:15 env: POSTGRES_USER: mmuser POSTGRES_PASSWORD: mostest diff --git a/e2e-tests/.ci/server.generate.sh b/e2e-tests/.ci/server.generate.sh index 6dc5624e64c..a3ccd9bc92a 100755 --- a/e2e-tests/.ci/server.generate.sh +++ b/e2e-tests/.ci/server.generate.sh @@ -93,7 +93,7 @@ $(for service in $ENABLED_DOCKER_SERVICES; do $(if mme2e_is_token_in_list "postgres" "$ENABLED_DOCKER_SERVICES"; then echo ' postgres: - image: mattermostdevelopment/mirrored-postgres:14 + image: mattermostdevelopment/mirrored-postgres:15 restart: "no" network_mode: host networks: !reset [] diff --git a/server/build/docker-compose.common.yml b/server/build/docker-compose.common.yml index b92631bf534..475464c30e0 100644 --- a/server/build/docker-compose.common.yml +++ b/server/build/docker-compose.common.yml @@ -5,7 +5,7 @@ x-logging: &default-logging services: postgres: - image: "postgres:14" + image: "postgres:15" logging: *default-logging restart: always networks: diff --git a/server/channels/store/sqlstore/store.go b/server/channels/store/sqlstore/store.go index bedd4b1efd3..49e2e722137 100644 --- a/server/channels/store/sqlstore/store.go +++ b/server/channels/store/sqlstore/store.go @@ -49,7 +49,7 @@ const ( // After 10, it's major and minor only. // 10.1 would be 100001. // 9.6.3 would be 90603. - minimumRequiredPostgresVersion = 140000 + minimumRequiredPostgresVersion = 150000 migrationsDirectionUp migrationDirection = "up" migrationsDirectionDown migrationDirection = "down" diff --git a/server/channels/store/sqlstore/store_test.go b/server/channels/store/sqlstore/store_test.go index d5a636a3dff..5ba889a4691 100644 --- a/server/channels/store/sqlstore/store_test.go +++ b/server/channels/store/sqlstore/store_test.go @@ -541,28 +541,37 @@ func TestCheckVersion(t *testing.T) { ver: "110001", wantLog: "Unsupported Postgres version", wantVersion: "11.1", - wantMinVersion: "14.0", + wantMinVersion: "15.0", }, { ver: "130001", wantLog: "Unsupported Postgres version", wantVersion: "13.1", - wantMinVersion: "14.0", + wantMinVersion: "15.0", }, { - ver: "140000", + ver: "140000", + wantLog: "Unsupported Postgres version", + wantVersion: "14.0", + wantMinVersion: "15.0", }, { - ver: "140019", + ver: "140019", + wantLog: "Unsupported Postgres version", + wantVersion: "14.19", + wantMinVersion: "15.0", }, { ver: "150000", }, + { + ver: "150019", + }, { ver: "90603", wantLog: "Unsupported Postgres version", wantVersion: "9.6.3", - wantMinVersion: "14.0", + wantMinVersion: "15.0", }, { ver: "12.34.1", diff --git a/server/docker-compose.makefile.yml b/server/docker-compose.makefile.yml index b8cd037d5cd..5c8d726954f 100644 --- a/server/docker-compose.makefile.yml +++ b/server/docker-compose.makefile.yml @@ -5,7 +5,7 @@ services: ports: - "5432:5432" volumes: - - "postgres-14-data:/var/lib/postgresql/data" + - "postgres-15-data:/var/lib/postgresql/data" extends: file: build/docker-compose.common.yml service: postgres @@ -125,4 +125,4 @@ networks: ip_range: 192.168.254.0/24 volumes: - postgres-14-data: + postgres-15-data: diff --git a/server/docker-compose.pgvector.yml b/server/docker-compose.pgvector.yml index 785cddc22f4..7b99998d046 100644 --- a/server/docker-compose.pgvector.yml +++ b/server/docker-compose.pgvector.yml @@ -3,6 +3,6 @@ services: postgres: # Override the postgres image to use pgvector - image: "pgvector/pgvector:pg14" + image: "pgvector/pgvector:pg15" # All other settings are inherited from the base service From eb3966e30bf4e13a0fdef43bb87429c54a6b7e25 Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Tue, 18 Aug 2026 08:25:17 -0300 Subject: [PATCH 5/5] Remove atmos/camo image proxy support (#37284) --- .../lib/src/server/default_config.ts | 2 - server/.golangci.yml | 1 - server/channels/api4/image_test.go | 21 -- server/channels/app/post_metadata.go | 4 +- server/channels/app/post_metadata_test.go | 4 +- server/channels/app/post_test.go | 50 +---- server/channels/app/server.go | 4 +- server/i18n/en.json | 14 +- .../services/imageproxy/atmos_camo.go | 90 --------- .../services/imageproxy/atmos_camo_test.go | 187 ------------------ .../services/imageproxy/imageproxy.go | 2 - .../services/imageproxy/imageproxy_test.go | 52 ----- server/public/model/config.go | 36 +--- server/public/model/config_test.go | 82 +++----- .../admin_console/admin_definition.tsx | 26 --- webapp/channels/src/i18n/en.json | 5 - webapp/platform/types/src/config.ts | 2 - 17 files changed, 42 insertions(+), 540 deletions(-) delete mode 100644 server/platform/services/imageproxy/atmos_camo.go delete mode 100644 server/platform/services/imageproxy/atmos_camo_test.go diff --git a/e2e-tests/playwright/lib/src/server/default_config.ts b/e2e-tests/playwright/lib/src/server/default_config.ts index c238fd653f4..b11d4f77ad0 100644 --- a/e2e-tests/playwright/lib/src/server/default_config.ts +++ b/e2e-tests/playwright/lib/src/server/default_config.ts @@ -775,8 +775,6 @@ const defaultServerConfig: AdminConfig = { ImageProxySettings: { Enable: false, ImageProxyType: 'local', - RemoteImageProxyURL: '', - RemoteImageProxyOptions: '', }, CloudSettings: { CWSURL: 'https://customers.mattermost.com', diff --git a/server/.golangci.yml b/server/.golangci.yml index 3f5521be8cc..c343d881eb8 100644 --- a/server/.golangci.yml +++ b/server/.golangci.yml @@ -215,7 +215,6 @@ linters: platform/services/cache/lru_test.go|\ platform/services/docextractor/combine.go|\ platform/services/docextractor/pdf.go|\ - platform/services/imageproxy/atmos_camo_test.go|\ platform/services/imageproxy/local.go|\ platform/services/imageproxy/local_test.go|\ platform/services/remotecluster/invitation.go|\ diff --git a/server/channels/api4/image_test.go b/server/channels/api4/image_test.go index 3ba3f72a621..c02cc1d603a 100644 --- a/server/channels/api4/image_test.go +++ b/server/channels/api4/image_test.go @@ -43,27 +43,6 @@ func TestGetImage(t *testing.T) { assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) - t.Run("atmos/camo", func(t *testing.T) { - imageURL := "http://foo.bar/baz.gif" - proxiedURL := "https://proxy.foo.bar/83d4d9ac78b76ce425ea67038826df867c62cc5c/687474703a2f2f666f6f2e6261722f62617a2e676966" - - th.App.UpdateConfig(func(cfg *model.Config) { - cfg.ImageProxySettings.Enable = new(true) - cfg.ImageProxySettings.ImageProxyType = new("atmos/camo") - cfg.ImageProxySettings.RemoteImageProxyOptions = new("7e5f3fab20b94782b43cdb022a66985ef28ba355df2c5d5da3c9a05e4b697bac") - cfg.ImageProxySettings.RemoteImageProxyURL = new("https://proxy.foo.bar") - }) - - r, err := http.NewRequest("GET", th.Client.APIURL+"/image?url="+url.QueryEscape(imageURL), nil) - require.NoError(t, err) - r.Header.Set(model.HeaderAuth, th.Client.AuthType+" "+th.Client.AuthToken) - - resp, err := th.Client.HTTPClient.Do(r) - require.NoError(t, err) - assert.Equal(t, http.StatusFound, resp.StatusCode) - assert.Equal(t, proxiedURL, resp.Header.Get("Location")) - }) - t.Run("local", func(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ImageProxySettings.Enable = new(true) diff --git a/server/channels/app/post_metadata.go b/server/channels/app/post_metadata.go index 543731cda8d..d96fb21ba45 100644 --- a/server/channels/app/post_metadata.go +++ b/server/channels/app/post_metadata.go @@ -43,9 +43,7 @@ func (s *Server) initPostMetadata() { // Dump any cached links if the proxy settings have changed so image URLs can be updated s.platform.AddConfigListener(func(before, after *model.Config) { if (before.ImageProxySettings.Enable != after.ImageProxySettings.Enable) || - (before.ImageProxySettings.ImageProxyType != after.ImageProxySettings.ImageProxyType) || - (before.ImageProxySettings.RemoteImageProxyURL != after.ImageProxySettings.RemoteImageProxyURL) || - (before.ImageProxySettings.RemoteImageProxyOptions != after.ImageProxySettings.RemoteImageProxyOptions) { + (before.ImageProxySettings.ImageProxyType != after.ImageProxySettings.ImageProxyType) { if err := platform.PurgeLinkCache(); err != nil { mlog.Warn("Failed to remove cached links when the proxy settings changed", mlog.Err(err)) } diff --git a/server/channels/app/post_metadata_test.go b/server/channels/app/post_metadata_test.go index 416ff786bd3..77719a7238c 100644 --- a/server/channels/app/post_metadata_test.go +++ b/server/channels/app/post_metadata_test.go @@ -1035,9 +1035,7 @@ func TestPreparePostForClientWithImageProxy(t *testing.T) { *cfg.ServiceSettings.SiteURL = "http://mymattermost.com" *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost,127.0.0.1" *cfg.ImageProxySettings.Enable = true - *cfg.ImageProxySettings.ImageProxyType = "atmos/camo" - *cfg.ImageProxySettings.RemoteImageProxyURL = "https://127.0.0.1" - *cfg.ImageProxySettings.RemoteImageProxyOptions = model.NewTestPassword() + *cfg.ImageProxySettings.ImageProxyType = "local" }) th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log()) diff --git a/server/channels/app/post_test.go b/server/channels/app/post_test.go index 1fbdef5d722..54016d03fbe 100644 --- a/server/channels/app/post_test.go +++ b/server/channels/app/post_test.go @@ -894,48 +894,12 @@ func TestImageProxy(t *testing.T) { th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log()) - testHMACKey := model.NewTestPassword() - for name, tc := range map[string]struct { ProxyType string - ProxyURL string - ProxyOptions string ImageURL string ProxiedImageURL string ProxiedRemovedImageURL string }{ - "atmos/camo": { - ProxyType: model.ImageProxyTypeAtmosCamo, - ProxyURL: "https://127.0.0.1", - ProxyOptions: testHMACKey, - ImageURL: "http://mydomain.com/myimage", - ProxiedRemovedImageURL: "http://mydomain.com/myimage", - ProxiedImageURL: "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage", - }, - "atmos/camo_SameSite": { - ProxyType: model.ImageProxyTypeAtmosCamo, - ProxyURL: "https://127.0.0.1", - ProxyOptions: testHMACKey, - ImageURL: "http://mymattermost.com/myimage", - ProxiedRemovedImageURL: "http://mymattermost.com/myimage", - ProxiedImageURL: "http://mymattermost.com/myimage", - }, - "atmos/camo_PathOnly": { - ProxyType: model.ImageProxyTypeAtmosCamo, - ProxyURL: "https://127.0.0.1", - ProxyOptions: testHMACKey, - ImageURL: "/myimage", - ProxiedRemovedImageURL: "http://mymattermost.com/myimage", - ProxiedImageURL: "http://mymattermost.com/myimage", - }, - "atmos/camo_EmptyImageURL": { - ProxyType: model.ImageProxyTypeAtmosCamo, - ProxyURL: "https://127.0.0.1", - ProxyOptions: testHMACKey, - ImageURL: "", - ProxiedRemovedImageURL: "", - ProxiedImageURL: "", - }, "local": { ProxyType: model.ImageProxyTypeLocal, ImageURL: "http://mydomain.com/myimage", @@ -965,8 +929,6 @@ func TestImageProxy(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ImageProxySettings.Enable = new(true) cfg.ImageProxySettings.ImageProxyType = new(tc.ProxyType) - cfg.ImageProxySettings.RemoteImageProxyOptions = new(tc.ProxyOptions) - cfg.ImageProxySettings.RemoteImageProxyURL = new(tc.ProxyURL) }) post := &model.Post{ @@ -1164,9 +1126,7 @@ func TestCreatePost(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SiteURL = "http://mymattermost.com" *cfg.ImageProxySettings.Enable = true - *cfg.ImageProxySettings.ImageProxyType = "atmos/camo" - *cfg.ImageProxySettings.RemoteImageProxyURL = "https://127.0.0.1" - *cfg.ImageProxySettings.RemoteImageProxyOptions = model.NewTestPassword() + *cfg.ImageProxySettings.ImageProxyType = "local" }) th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log()) @@ -2029,9 +1989,7 @@ func TestPatchPost(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SiteURL = "http://mymattermost.com" *cfg.ImageProxySettings.Enable = true - *cfg.ImageProxySettings.ImageProxyType = "atmos/camo" - *cfg.ImageProxySettings.RemoteImageProxyURL = "https://127.0.0.1" - *cfg.ImageProxySettings.RemoteImageProxyOptions = model.NewTestPassword() + *cfg.ImageProxySettings.ImageProxyType = "local" }) th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log()) @@ -2490,9 +2448,7 @@ func TestUpdatePost(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SiteURL = "http://mymattermost.com" *cfg.ImageProxySettings.Enable = true - *cfg.ImageProxySettings.ImageProxyType = "atmos/camo" - *cfg.ImageProxySettings.RemoteImageProxyURL = "https://127.0.0.1" - *cfg.ImageProxySettings.RemoteImageProxyOptions = model.NewTestPassword() + *cfg.ImageProxySettings.ImageProxyType = "local" }) th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log()) diff --git a/server/channels/app/server.go b/server/channels/app/server.go index 63fce4b224c..0f70afa9c09 100644 --- a/server/channels/app/server.go +++ b/server/channels/app/server.go @@ -598,9 +598,7 @@ func NewServer(options ...Option) (*Server, error) { // Dump the image cache if the proxy settings have changed. (need switch URLs to the correct proxy) s.platform.AddConfigListener(func(oldCfg, newCfg *model.Config) { if (oldCfg.ImageProxySettings.Enable != newCfg.ImageProxySettings.Enable) || - (oldCfg.ImageProxySettings.ImageProxyType != newCfg.ImageProxySettings.ImageProxyType) || - (oldCfg.ImageProxySettings.RemoteImageProxyURL != newCfg.ImageProxySettings.RemoteImageProxyURL) || - (oldCfg.ImageProxySettings.RemoteImageProxyOptions != newCfg.ImageProxySettings.RemoteImageProxyOptions) { + (oldCfg.ImageProxySettings.ImageProxyType != newCfg.ImageProxySettings.ImageProxyType) { if err = s.openGraphDataCache.Purge(); err != nil { mlog.Error("Failed to purge Open Graph data cache after config change", mlog.Err(err)) } diff --git a/server/i18n/en.json b/server/i18n/en.json index 60e09e38124..a0b96045a63 100644 --- a/server/i18n/en.json +++ b/server/i18n/en.json @@ -11719,16 +11719,8 @@ "translation": "Invalid timeout value {{.Value}}. Should be a positive number." }, { - "id": "model.config.is_valid.atmos_camo_image_proxy_options.app_error", - "translation": "Invalid RemoteImageProxyOptions for atmos/camo. Must be set to your shared key." - }, - { - "id": "model.config.is_valid.atmos_camo_image_proxy_options_length.app_error", - "translation": "Invalid RemoteImageProxyOptions for atmos/camo: HMAC key must be at least {{.MinLength}} bytes for FIPS compliance." - }, - { - "id": "model.config.is_valid.atmos_camo_image_proxy_url.app_error", - "translation": "Invalid RemoteImageProxyURL for atmos/camo. Must be set to your shared key." + "id": "model.config.is_valid.atmos_camo_image_proxy_removed.app_error", + "translation": "The 'atmos/camo' image proxy type was removed in Mattermost v12.0. Set ImageProxySettings.ImageProxyType to 'local', or set ImageProxySettings.Enable to false to disable the image proxy." }, { "id": "model.config.is_valid.autotranslation.agents.llm_service_id.app_error", @@ -11992,7 +11984,7 @@ }, { "id": "model.config.is_valid.image_proxy_type.app_error", - "translation": "Invalid image proxy type. Must be 'local' or 'atmos/camo'." + "translation": "Invalid image proxy type. Must be 'local'." }, { "id": "model.config.is_valid.import.directory.app_error", diff --git a/server/platform/services/imageproxy/atmos_camo.go b/server/platform/services/imageproxy/atmos_camo.go deleted file mode 100644 index 9e9d1f89c9a..00000000000 --- a/server/platform/services/imageproxy/atmos_camo.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package imageproxy - -import ( - "crypto/hmac" - "crypto/sha1" - "encoding/hex" - "io" - "net/http" - "net/url" - - "github.com/mattermost/mattermost/server/public/model" -) - -type AtmosCamoBackend struct { - siteURL *url.URL - remoteOptions string - remoteURL *url.URL - client *http.Client -} - -func makeAtmosCamoBackend(proxy *ImageProxy, proxySettings model.ImageProxySettings) *AtmosCamoBackend { - // We deliberately ignore the error because it's from config.json. - // The function returns a nil pointer in case of error, and we handle it when it's used. - remoteURL, _ := url.Parse(*proxySettings.RemoteImageProxyURL) - - return &AtmosCamoBackend{ - siteURL: proxy.siteURL, - remoteURL: remoteURL, - remoteOptions: *proxySettings.RemoteImageProxyOptions, - client: proxy.HTTPService.MakeClient(false), - } -} - -func (backend *AtmosCamoBackend) GetImage(w http.ResponseWriter, r *http.Request, imageURL string) { - http.Redirect(w, r, backend.getAtmosCamoImageURL(imageURL), http.StatusFound) -} - -func (backend *AtmosCamoBackend) GetImageDirect(imageURL string) (io.ReadCloser, string, error) { - req, err := http.NewRequest("GET", backend.getAtmosCamoImageURL(imageURL), nil) - if err != nil { - return nil, "", Error{err} - } - - resp, err := backend.client.Do(req) - if err != nil { - return nil, "", Error{err} - } - - // Note that we don't do any additional validation of the received data since we expect the image proxy to do that - return resp.Body, resp.Header.Get("Content-Type"), nil -} - -func (backend *AtmosCamoBackend) getAtmosCamoImageURL(imageURL string) string { - if imageURL == "" || backend.siteURL == nil { - return imageURL - } - - // Parse url, return siteURL in case of failure. - // Also if the URL is opaque. - parsedURL, err := url.Parse(imageURL) - if err != nil || parsedURL.Opaque != "" { - return backend.siteURL.String() - } - - // If host is same as siteURL host/ remoteURL host, return. - if parsedURL.Host == backend.siteURL.Host || parsedURL.Host == backend.remoteURL.Host { - return parsedURL.String() - } - - // Handle protocol-relative URLs. - if parsedURL.Scheme == "" { - parsedURL.Scheme = backend.siteURL.Scheme - } - - // If it's a relative URL, fill up the hostname and scheme and return. - if parsedURL.Host == "" { - parsedURL.Host = backend.siteURL.Host - return parsedURL.String() - } - - urlBytes := []byte(parsedURL.String()) - mac := hmac.New(sha1.New, []byte(backend.remoteOptions)) - mac.Write(urlBytes) - digest := hex.EncodeToString(mac.Sum(nil)) - - return backend.remoteURL.String() + "/" + digest + "/" + hex.EncodeToString(urlBytes) -} diff --git a/server/platform/services/imageproxy/atmos_camo_test.go b/server/platform/services/imageproxy/atmos_camo_test.go deleted file mode 100644 index 94959d94dfb..00000000000 --- a/server/platform/services/imageproxy/atmos_camo_test.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package imageproxy - -import ( - "io" - "net/http" - "net/http/httptest" - "net/url" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/mattermost/mattermost/server/public/model" - "github.com/mattermost/mattermost/server/public/shared/httpservice" - "github.com/mattermost/mattermost/server/v8/channels/utils/testutils" -) - -func makeTestAtmosCamoProxy() *ImageProxy { - configService := &testutils.StaticConfigService{ - Cfg: &model.Config{ - ServiceSettings: model.ServiceSettings{ - SiteURL: new("https://mattermost.example.com"), - AllowedUntrustedInternalConnections: new("127.0.0.1"), - }, - ImageProxySettings: model.ImageProxySettings{ - Enable: new(true), - ImageProxyType: model.NewPointer(model.ImageProxyTypeAtmosCamo), - RemoteImageProxyURL: new("http://images.example.com"), - RemoteImageProxyOptions: new("7e5f3fab20b94782b43cdb022a66985ef28ba355df2c5d5da3c9a05e4b697bac"), - }, - }, - } - - return MakeImageProxy(configService, httpservice.MakeHTTPService(configService), nil) -} - -func TestAtmosCamoBackend_GetImage(t *testing.T) { - imageURL := "https://www.mattermost.com/wp-content/uploads/2022/02/logoHorizontalWhite.png" - proxiedURL := "http://images.example.com/b569ce17f1be4550cffa8d8dd3a9e80e6d209584/68747470733a2f2f7777772e6d61747465726d6f73742e636f6d2f77702d636f6e74656e742f75706c6f6164732f323032322f30322f6c6f676f486f72697a6f6e74616c57686974652e706e67" - - proxy := makeTestAtmosCamoProxy() - - recorder := httptest.NewRecorder() - request, _ := http.NewRequest(http.MethodGet, "", nil) - proxy.GetImage(recorder, request, imageURL) - resp := recorder.Result() - - assert.Equal(t, http.StatusFound, resp.StatusCode) - assert.Equal(t, proxiedURL, resp.Header.Get("Location")) -} - -func TestAtmosCamoBackend_GetImageDirect(t *testing.T) { - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Cache-Control", "max-age=2592000, private") - w.Header().Set("Content-Type", "image/png") - w.Header().Set("Content-Length", "10") - - w.WriteHeader(http.StatusOK) - w.Write([]byte("1111111111")) - }) - - mock := httptest.NewServer(handler) - defer mock.Close() - - proxy := makeTestAtmosCamoProxy() - parsedURL, err := url.Parse("https://mattermost.example.com") - require.NoError(t, err) - - remoteURL, err := url.Parse(mock.URL) - require.NoError(t, err) - - backend := &AtmosCamoBackend{ - siteURL: parsedURL, - remoteURL: remoteURL, - client: proxy.HTTPService.MakeClient(false), - } - - body, contentType, err := backend.GetImageDirect("https://example.com/image.png") - - assert.NoError(t, err) - assert.Equal(t, "image/png", contentType) - - require.NotNil(t, body) - respBody, _ := io.ReadAll(body) - assert.Equal(t, []byte("1111111111"), respBody) -} - -func TestGetAtmosCamoImageURL(t *testing.T) { - imageURL := "https://mattermost.com/wp-content/uploads/2022/02/logoHorizontal.png" - proxiedURL := "http://images.example.com/03b122734ae088d10cb46ea05512ec7dc852299e/68747470733a2f2f6d61747465726d6f73742e636f6d2f77702d636f6e74656e742f75706c6f6164732f323032322f30322f6c6f676f486f72697a6f6e74616c2e706e67" - - defaultSiteURL := "https://mattermost.example.com" - proxyURL := "http://images.example.com" - - for _, test := range []struct { - Name string - Input string - SiteURL string - Expected string - }{ - { - Name: "should proxy image", - Input: imageURL, - SiteURL: defaultSiteURL, - Expected: proxiedURL, - }, - { - Name: "should proxy image when no site URL is set", - Input: imageURL, - SiteURL: "", - Expected: proxiedURL, - }, - { - Name: "should proxy image when a site URL with a subpath is set", - Input: imageURL, - SiteURL: proxyURL + "/subpath", - Expected: proxiedURL, - }, - { - Name: "should not proxy a relative image", - Input: "/static/logo.png", - SiteURL: defaultSiteURL, - Expected: "https://mattermost.example.com/static/logo.png", - }, - { - Name: "should bypass opaque URLs", - Input: "http:xyz123?query", - SiteURL: defaultSiteURL, - Expected: defaultSiteURL, - }, - { - Name: "should not proxy an image on the Mattermost server", - Input: "https://mattermost.example.com/static/logo.png", - SiteURL: defaultSiteURL, - Expected: "https://mattermost.example.com/static/logo.png", - }, - { - Name: "should not proxy an image on the Mattermost server when a subpath is set", - Input: "https://mattermost.example.com/static/logo.png", - SiteURL: defaultSiteURL + "/static", - Expected: "https://mattermost.example.com/static/logo.png", - }, - { - Name: "should not proxy an image that has already been proxied", - Input: proxiedURL, - SiteURL: defaultSiteURL, - Expected: proxiedURL, - }, - { - Name: "should not bypass protocol relative URLs", - Input: "https://mattermost.com/wp-content/uploads/2022/02/logoHorizontal.png", - SiteURL: "http://mattermost.example.com", - Expected: proxiedURL, - }, - { - Name: "should not bypass if the host prefix is same", - Input: "https://mattermost.com/wp-content/uploads/2022/02/logoHorizontal.png", - SiteURL: defaultSiteURL, - Expected: "http://images.example.com/03b122734ae088d10cb46ea05512ec7dc852299e/68747470733a2f2f6d61747465726d6f73742e636f6d2f77702d636f6e74656e742f75706c6f6164732f323032322f30322f6c6f676f486f72697a6f6e74616c2e706e67", - }, - { - Name: "should not bypass for user auth URLs", - Input: "https://mattermost.com/wp-content/uploads/2022/02/logoHorizontal.png", - SiteURL: defaultSiteURL, - Expected: "http://images.example.com/03b122734ae088d10cb46ea05512ec7dc852299e/68747470733a2f2f6d61747465726d6f73742e636f6d2f77702d636f6e74656e742f75706c6f6164732f323032322f30322f6c6f676f486f72697a6f6e74616c2e706e67", - }, - } { - t.Run(test.Name, func(t *testing.T) { - parsedURL, err := url.Parse(test.SiteURL) - require.NoError(t, err) - - remoteURL, err := url.Parse(proxyURL) - require.NoError(t, err) - - backend := &AtmosCamoBackend{ - siteURL: parsedURL, - remoteURL: remoteURL, - remoteOptions: *makeTestAtmosCamoProxy().ConfigService.Config().ImageProxySettings.RemoteImageProxyOptions, - } - - assert.Equal(t, test.Expected, backend.getAtmosCamoImageURL(test.Input)) - }) - } -} diff --git a/server/platform/services/imageproxy/imageproxy.go b/server/platform/services/imageproxy/imageproxy.go index a62a49336c5..3521b630699 100644 --- a/server/platform/services/imageproxy/imageproxy.go +++ b/server/platform/services/imageproxy/imageproxy.go @@ -73,8 +73,6 @@ func (proxy *ImageProxy) makeBackend(proxySettings model.ImageProxySettings) Ima switch *proxySettings.ImageProxyType { case model.ImageProxyTypeLocal: return makeLocalBackend(proxy) - case model.ImageProxyTypeAtmosCamo: - return makeAtmosCamoBackend(proxy, proxySettings) default: return nil } diff --git a/server/platform/services/imageproxy/imageproxy_test.go b/server/platform/services/imageproxy/imageproxy_test.go index b87b6d3d75b..d78d24f518b 100644 --- a/server/platform/services/imageproxy/imageproxy_test.go +++ b/server/platform/services/imageproxy/imageproxy_test.go @@ -7,7 +7,6 @@ import ( "net/url" "testing" - "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/v8/channels/utils/testutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -119,26 +118,6 @@ func TestGetUnproxiedImageURL(t *testing.T) { } func TestOnConfigChange(t *testing.T) { - t.Run("should switch between backends", func(t *testing.T) { - proxy := makeTestAtmosCamoProxy() - - require.Equal(t, "https://mattermost.example.com", proxy.backend.(*AtmosCamoBackend).siteURL.String()) - - newConfig := proxy.ConfigService.Config().Clone() - newConfig.ImageProxySettings.ImageProxyType = model.NewPointer(model.ImageProxyTypeLocal) - - proxy.ConfigService.(*testutils.StaticConfigService).UpdateConfig(newConfig) - - require.Equal(t, "https://mattermost.example.com", proxy.backend.(*LocalBackend).baseURL.String()) - - newConfig = proxy.ConfigService.Config().Clone() - newConfig.ImageProxySettings.ImageProxyType = model.NewPointer(model.ImageProxyTypeAtmosCamo) - - proxy.ConfigService.(*testutils.StaticConfigService).UpdateConfig(newConfig) - - require.Equal(t, "https://mattermost.example.com", proxy.backend.(*AtmosCamoBackend).siteURL.String()) - }) - t.Run("for local proxy, should update site URL when that changes", func(t *testing.T) { proxy := makeTestLocalProxy() @@ -153,35 +132,4 @@ func TestOnConfigChange(t *testing.T) { require.Equal(t, "https://new.example.com", proxy.siteURL.String()) require.Equal(t, "https://new.example.com", proxy.backend.(*LocalBackend).baseURL.String()) }) - - t.Run("for atmos/camo proxy, should update site URL when that changes", func(t *testing.T) { - proxy := makeTestAtmosCamoProxy() - - require.Equal(t, "https://mattermost.example.com", proxy.siteURL.String()) - require.Equal(t, "https://mattermost.example.com", proxy.backend.(*AtmosCamoBackend).siteURL.String()) - - newConfig := proxy.ConfigService.Config().Clone() - newConfig.ServiceSettings.SiteURL = new("https://new.example.com") - - proxy.ConfigService.(*testutils.StaticConfigService).UpdateConfig(newConfig) - - require.Equal(t, "https://new.example.com", proxy.siteURL.String()) - require.Equal(t, "https://new.example.com", proxy.backend.(*AtmosCamoBackend).siteURL.String()) - }) - - t.Run("for atmos/camo proxy, should update additional options when those change", func(t *testing.T) { - proxy := makeTestAtmosCamoProxy() - - require.Equal(t, "http://images.example.com", proxy.backend.(*AtmosCamoBackend).remoteURL.String()) - // require.Equal(t, "7e5f3fab20b94782b43cdb022a66985ef28ba355df2c5d5da3c9a05e4b697bac", proxy.backend.(*AtmosCamoBackend).remoteOptions) - - newConfig := proxy.ConfigService.Config().Clone() - newConfig.ImageProxySettings.RemoteImageProxyURL = new("https://new.example.com") - newConfig.ImageProxySettings.RemoteImageProxyOptions = new("some other random hash") - - proxy.ConfigService.(*testutils.StaticConfigService).UpdateConfig(newConfig) - - require.Equal(t, "https://new.example.com", proxy.backend.(*AtmosCamoBackend).remoteURL.String()) - // require.Equal(t, "some other random hash", proxy.backend.(*AtmosCamoBackend).remoteOptions) - }) } diff --git a/server/public/model/config.go b/server/public/model/config.go index 7eb27e479bf..0a001809fc6 100644 --- a/server/public/model/config.go +++ b/server/public/model/config.go @@ -286,8 +286,8 @@ const ( GlobalrelayCustomerTypeA10 = "A10" GlobalrelayCustomerTypeCustom = "CUSTOM" - ImageProxyTypeLocal = "local" - ImageProxyTypeAtmosCamo = "atmos/camo" + ImageProxyTypeLocal = "local" + ImageProxyTypeLegacyAtmosCamo = "atmos/camo" GoogleSettingsDefaultScope = "profile email" GoogleSettingsDefaultAuthEndpoint = "https://accounts.google.com/o/oauth2/v2/auth" @@ -3986,10 +3986,8 @@ func (s *GuestAccountsSettings) IsValid() *AppError { } type ImageProxySettings struct { - Enable *bool `access:"environment_image_proxy"` - ImageProxyType *string `access:"environment_image_proxy"` - RemoteImageProxyURL *string `access:"environment_image_proxy"` - RemoteImageProxyOptions *string `access:"environment_image_proxy"` + Enable *bool `access:"environment_image_proxy"` + ImageProxyType *string `access:"environment_image_proxy"` } func (s *ImageProxySettings) SetDefaults() { @@ -4000,14 +3998,6 @@ func (s *ImageProxySettings) SetDefaults() { if s.ImageProxyType == nil { s.ImageProxyType = new(ImageProxyTypeLocal) } - - if s.RemoteImageProxyURL == nil { - s.RemoteImageProxyURL = new("") - } - - if s.RemoteImageProxyOptions == nil { - s.RemoteImageProxyOptions = new("") - } } // ImportSettings defines configuration settings for file imports. @@ -5295,24 +5285,14 @@ func (s *DisplaySettings) isValid() *AppError { } func (s *ImageProxySettings) isValid() *AppError { + if *s.ImageProxyType == ImageProxyTypeLegacyAtmosCamo { + return NewAppError("Config.IsValid", "model.config.is_valid.atmos_camo_image_proxy_removed.app_error", nil, "", http.StatusBadRequest) + } + if *s.Enable { switch *s.ImageProxyType { case ImageProxyTypeLocal: // No other settings to validate - case ImageProxyTypeAtmosCamo: - if *s.RemoteImageProxyURL == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.atmos_camo_image_proxy_url.app_error", nil, "", http.StatusBadRequest) - } - - if *s.RemoteImageProxyOptions == "" { - return NewAppError("Config.IsValid", "model.config.is_valid.atmos_camo_image_proxy_options.app_error", nil, "", http.StatusBadRequest) - } - - // RemoteImageProxyOptions is used as the HMAC key for URL signing, - // so it is subject to the same FIPS minimum key length as passwords. - if FIPSEnabled && len(*s.RemoteImageProxyOptions) < PasswordFIPSMinimumLength { - return NewAppError("Config.IsValid", "model.config.is_valid.atmos_camo_image_proxy_options_length.app_error", map[string]any{"MinLength": PasswordFIPSMinimumLength}, "", http.StatusBadRequest) - } default: return NewAppError("Config.IsValid", "model.config.is_valid.image_proxy_type.app_error", nil, "", http.StatusBadRequest) } diff --git a/server/public/model/config_test.go b/server/public/model/config_test.go index 38994f4c10e..263eb99957a 100644 --- a/server/public/model/config_test.go +++ b/server/public/model/config_test.go @@ -1354,21 +1354,15 @@ func TestImageProxySettingsSetDefaults(t *testing.T) { assert.Equal(t, false, *ips.Enable) assert.Equal(t, ImageProxyTypeLocal, *ips.ImageProxyType) - assert.Equal(t, "", *ips.RemoteImageProxyURL) - assert.Equal(t, "", *ips.RemoteImageProxyOptions) }) } func TestImageProxySettingsIsValid(t *testing.T) { - testHMACKey := NewTestPassword() - for _, test := range []struct { - Name string - Enable bool - ImageProxyType string - RemoteImageProxyURL string - RemoteImageProxyOptions string - ExpectError bool + Name string + Enable bool + ImageProxyType string + ExpectError bool }{ { Name: "disabled", @@ -1376,66 +1370,40 @@ func TestImageProxySettingsIsValid(t *testing.T) { ExpectError: false, }, { - Name: "disabled with bad values", - Enable: false, - ImageProxyType: "garbage", - RemoteImageProxyURL: "garbage", - RemoteImageProxyOptions: "garbage", - ExpectError: false, + Name: "disabled with bad values", + Enable: false, + ImageProxyType: "garbage", + ExpectError: false, }, { - Name: "missing type", - Enable: true, - ImageProxyType: "", + Name: "atmos/camo, disabled", + Enable: false, + ImageProxyType: ImageProxyTypeLegacyAtmosCamo, ExpectError: true, }, { - Name: "local", - Enable: true, - ImageProxyType: "local", - RemoteImageProxyURL: "garbage", - RemoteImageProxyOptions: "garbage", - ExpectError: false, - }, - { - Name: "atmos/camo", - Enable: true, - ImageProxyType: ImageProxyTypeAtmosCamo, - RemoteImageProxyURL: "someurl", - RemoteImageProxyOptions: testHMACKey, - ExpectError: false, - }, - { - Name: "atmos/camo, missing url", - Enable: true, - ImageProxyType: ImageProxyTypeAtmosCamo, - RemoteImageProxyURL: "", - RemoteImageProxyOptions: "garbage", - ExpectError: true, + Name: "atmos/camo, enabled", + Enable: true, + ImageProxyType: ImageProxyTypeLegacyAtmosCamo, + ExpectError: true, }, { - Name: "atmos/camo, missing options", - Enable: true, - ImageProxyType: ImageProxyTypeAtmosCamo, - RemoteImageProxyURL: "someurl", - RemoteImageProxyOptions: "", - ExpectError: true, + Name: "missing type", + Enable: true, + ImageProxyType: "", + ExpectError: true, }, { - Name: "atmos/camo, short options under FIPS", - Enable: true, - ImageProxyType: ImageProxyTypeAtmosCamo, - RemoteImageProxyURL: "someurl", - RemoteImageProxyOptions: "foo", - ExpectError: FIPSEnabled, + Name: "local", + Enable: true, + ImageProxyType: "local", + ExpectError: false, }, } { t.Run(test.Name, func(t *testing.T) { ips := &ImageProxySettings{ - Enable: &test.Enable, - ImageProxyType: &test.ImageProxyType, - RemoteImageProxyURL: &test.RemoteImageProxyURL, - RemoteImageProxyOptions: &test.RemoteImageProxyOptions, + Enable: &test.Enable, + ImageProxyType: &test.ImageProxyType, } appErr := ips.isValid() diff --git a/webapp/channels/src/components/admin_console/admin_definition.tsx b/webapp/channels/src/components/admin_console/admin_definition.tsx index 6cfad58941f..9861b2d3ae6 100644 --- a/webapp/channels/src/components/admin_console/admin_definition.tsx +++ b/webapp/channels/src/components/admin_console/admin_definition.tsx @@ -1673,10 +1673,6 @@ const AdminDefinition: AdminDefinitionType = { }, help_text_markdown: false, options: [ - { - value: 'atmos/camo', - display_name: defineMessage({id: 'atmos/camo', defaultMessage: 'atmos/camo'}), - }, { value: 'local', display_name: defineMessage({id: 'local', defaultMessage: 'local'}), @@ -1687,28 +1683,6 @@ const AdminDefinition: AdminDefinitionType = { it.stateIsFalse('ImageProxySettings.Enable'), ), }, - { - type: 'text', - key: 'ImageProxySettings.RemoteImageProxyURL', - label: defineMessage({id: 'admin.image.proxyURL', defaultMessage: 'Remote Image Proxy URL:'}), - help_text: defineMessage({id: 'admin.image.proxyURLDescription', defaultMessage: 'URL of your remote image proxy server.'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.IMAGE_PROXY)), - it.stateIsFalse('ImageProxySettings.Enable'), - it.stateEquals('ImageProxySettings.ImageProxyType', 'local'), - ), - }, - { - type: 'text', - key: 'ImageProxySettings.RemoteImageProxyOptions', - label: defineMessage({id: 'admin.image.proxyOptions', defaultMessage: 'Remote Image Proxy Options:'}), - help_text: defineMessage({id: 'admin.image.proxyOptionsDescription', defaultMessage: 'Additional options such as the URL signing key. Refer to your image proxy documentation to learn more about what options are supported.'}), - isDisabled: it.any( - it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ENVIRONMENT.IMAGE_PROXY)), - it.stateIsFalse('ImageProxySettings.Enable'), - it.stateEquals('ImageProxySettings.ImageProxyType', 'local'), - ), - }, ], }, }, diff --git a/webapp/channels/src/i18n/en.json b/webapp/channels/src/i18n/en.json index 11033df694e..e71415c704e 100644 --- a/webapp/channels/src/i18n/en.json +++ b/webapp/channels/src/i18n/en.json @@ -1689,12 +1689,8 @@ "admin.image.maxFileSizeDescription": "Maximum file size for message attachments in megabytes. Caution: Verify server memory can support your setting choice. Large file sizes increase the risk of server crashes and failed uploads due to network interruptions.", "admin.image.maxFileSizeExample": "50", "admin.image.maxFileSizeTitle": "Maximum File Size:", - "admin.image.proxyOptions": "Remote Image Proxy Options:", - "admin.image.proxyOptionsDescription": "Additional options such as the URL signing key. Refer to your image proxy documentation to learn more about what options are supported.", "admin.image.proxyType": "Image Proxy Type:", "admin.image.proxyTypeDescription": "Configure an image proxy to load all Markdown images through a proxy. The image proxy prevents users from making insecure image requests, provides caching for increased performance, and automates image adjustments such as resizing. See documentation to learn more.", - "admin.image.proxyURL": "Remote Image Proxy URL:", - "admin.image.proxyURLDescription": "URL of your remote image proxy server.", "admin.image.publicLinkDescription": "32-character salt added to signing of public links. Randomly generated on install. Select \"Regenerate\" to create new salt.", "admin.image.publicLinkTitle": "Public Link Salt:", "admin.image.shareDescription": "Allow users to share public links to files and images.", @@ -4191,7 +4187,6 @@ "apps.suggestion.no_dynamic": "No data was returned for dynamic suggestions", "apps.suggestion.no_static": "No matching options.", "apps.suggestion.no_suggestion": "No matching suggestions.", - "atmos/camo": "atmos/camo", "attachment.footerIconAltText": "Attachment footer icon", "attachment.imageAltText": "Attachment image", "audit_table.accountActive": "Account activated", diff --git a/webapp/platform/types/src/config.ts b/webapp/platform/types/src/config.ts index 59b3c1f0db5..beee225ba47 100644 --- a/webapp/platform/types/src/config.ts +++ b/webapp/platform/types/src/config.ts @@ -1027,8 +1027,6 @@ export type GuestAccountsSettings = { export type ImageProxySettings = { Enable: boolean; ImageProxyType: string; - RemoteImageProxyURL: string; - RemoteImageProxyOptions: string; }; export type CloudSettings = {