Maintainers
+Maintainers
This module is maintained by the OCA.
diff --git a/webservice/tests/__init__.py b/webservice/tests/__init__.py
index 70d7ce42..88a07b8f 100644
--- a/webservice/tests/__init__.py
+++ b/webservice/tests/__init__.py
@@ -1,3 +1,4 @@
from . import test_oauth2
from . import test_webservice
from . import test_utils
+from . import test_neutralize
diff --git a/webservice/tests/test_neutralize.py b/webservice/tests/test_neutralize.py
new file mode 100644
index 00000000..e1e10959
--- /dev/null
+++ b/webservice/tests/test_neutralize.py
@@ -0,0 +1,49 @@
+# Copyright 2026 Camptocamp SA
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo.modules import neutralize
+from odoo.tests import tagged
+
+from .common import CommonWebService
+
+
+@tagged("neutralize")
+class TestWebserviceNeutralize(CommonWebService):
+ @classmethod
+ def _setup_records(cls):
+ res = super()._setup_records()
+ # `auth_type="none"` skips the `_check_auth_type` constraint so all
+ # credential fields can be populated at once regardless of which
+ # auth method would normally require them.
+ cls.backend = cls.env["webservice.backend"].create(
+ {
+ "name": "Neutralize WebService",
+ "protocol": "http",
+ "url": "https://localhost.demo.odoo/",
+ "tech_name": "neutralize_ws",
+ "auth_type": "none",
+ "username": "secret-user",
+ "password": "secret-password",
+ "api_key": "secret-api-key",
+ "oauth2_clientid": "secret-client-id",
+ "oauth2_client_secret": "secret-client-secret",
+ "oauth2_client_auth_value": "secret-header-value",
+ "oauth2_token": '{"access_token": "secret-token"}',
+ }
+ )
+ return res
+
+ def test_neutralize_removes_webservice_credentials(self):
+ """Test database neutralization clears stored webservice backend credentials."""
+ installed_modules = neutralize.get_installed_modules(self.cr)
+ queries = neutralize.get_neutralization_queries(installed_modules)
+ for query in queries:
+ self.cr.execute(query)
+ self.backend.invalidate_recordset()
+ self.assertFalse(self.backend.username)
+ self.assertFalse(self.backend.password)
+ self.assertFalse(self.backend.api_key)
+ self.assertFalse(self.backend.oauth2_clientid)
+ self.assertFalse(self.backend.oauth2_client_secret)
+ self.assertFalse(self.backend.oauth2_client_auth_value)
+ self.assertFalse(self.backend.oauth2_token)
diff --git a/webservice_server_env/README.rst b/webservice_server_env/README.rst
index 657979b9..35d7bb6e 100644
--- a/webservice_server_env/README.rst
+++ b/webservice_server_env/README.rst
@@ -40,6 +40,14 @@ Webservice addon.
.. contents::
:local:
+Configuration
+=============
+
+When a database is neutralized, webservice backend credentials
+(username, password, API key, OAuth2 client id/secret, custom OAuth2
+auth header value) stored as server environment defaults are cleared as
+well.
+
Bug Tracker
===========
diff --git a/webservice_server_env/data/neutralize.sql b/webservice_server_env/data/neutralize.sql
new file mode 100644
index 00000000..ae619e29
--- /dev/null
+++ b/webservice_server_env/data/neutralize.sql
@@ -0,0 +1,16 @@
+-- remove webservice backend credentials stored as server environment defaults
+-- (this module turns username/password/api_key/oauth2_clientid/oauth2_client_secret
+-- into non-stored fields backed by the server_env_defaults JSON column
+-- this neutralization is needed if both webservice and server_environment are installed,
+-- so the plain columns nulled by webservice/data/neutralize.sql can be properly cleared)
+UPDATE webservice_backend
+ SET server_env_defaults = (
+ server_env_defaults::jsonb
+ - 'x_username_env_default'
+ - 'x_password_env_default'
+ - 'x_api_key_env_default'
+ - 'x_oauth2_clientid_env_default'
+ - 'x_oauth2_client_secret_env_default'
+ - 'x_oauth2_client_auth_value_env_default'
+ )::text
+ WHERE server_env_defaults IS NOT NULL;
diff --git a/webservice_server_env/readme/CONFIGURE.md b/webservice_server_env/readme/CONFIGURE.md
new file mode 100644
index 00000000..6c80484f
--- /dev/null
+++ b/webservice_server_env/readme/CONFIGURE.md
@@ -0,0 +1,3 @@
+When a database is neutralized, webservice backend credentials (username,
+password, API key, OAuth2 client id/secret, custom OAuth2 auth header
+value) stored as server environment defaults are cleared as well.
diff --git a/webservice_server_env/static/description/index.html b/webservice_server_env/static/description/index.html
index 053d2fa2..69aa880a 100644
--- a/webservice_server_env/static/description/index.html
+++ b/webservice_server_env/static/description/index.html
@@ -380,17 +380,25 @@ WebService Server Environment
Table of contents
Configuration
+When a database is neutralized, webservice backend credentials +(username, password, API key, OAuth2 client id/secret, custom OAuth2 +auth header value) stored as server environment defaults are cleared as +well.
+Bug Tracker
+Bug Tracker
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -398,23 +406,23 @@
Bug Tracker
Do not contact contributors directly about support or help with technical issues.
Credits
+Credits
Contributors
+Contributors
- Enric Tobella <etobella@creublanca.es>
- Daniel Reis <dreis@opensourceintegrators.com>
Maintainers
+Maintainers
This module is maintained by the OCA.
diff --git a/webservice_server_env/tests/__init__.py b/webservice_server_env/tests/__init__.py
index 38fb5cf0..e228ba19 100644
--- a/webservice_server_env/tests/__init__.py
+++ b/webservice_server_env/tests/__init__.py
@@ -1 +1,2 @@
from . import test_oauth2
+from . import test_neutralize
diff --git a/webservice_server_env/tests/test_neutralize.py b/webservice_server_env/tests/test_neutralize.py
new file mode 100644
index 00000000..906630e0
--- /dev/null
+++ b/webservice_server_env/tests/test_neutralize.py
@@ -0,0 +1,47 @@
+# Copyright 2026 Camptocamp SA
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo.modules import neutralize
+from odoo.tests import tagged
+
+from odoo.addons.webservice.tests.common import CommonWebService
+
+
+@tagged("neutralize")
+class TestWebserviceServerEnvNeutralize(CommonWebService):
+ @classmethod
+ def _setup_records(cls):
+ res = super()._setup_records()
+ # No `SERVER_ENV_CONFIG` section is defined for this tech_name, so
+ # these credentials are stored as env-default values inside
+ # `server_env_defaults` instead of the (now unused) plain columns.
+ cls.backend = cls.env["webservice.backend"].create(
+ {
+ "name": "Neutralize WebService Server Env",
+ "protocol": "http",
+ "url": "https://localhost.demo.odoo/",
+ "tech_name": "neutralize_server_env_ws",
+ "auth_type": "none",
+ "username": "secret-user",
+ "password": "secret-password",
+ "api_key": "secret-api-key",
+ "oauth2_clientid": "secret-client-id",
+ "oauth2_client_secret": "secret-client-secret",
+ "oauth2_client_auth_value": "secret-header-value",
+ }
+ )
+ return res
+
+ def test_neutralize_removes_server_env_default_credentials(self):
+ """Test neutralization clears credentials stored as server-env defaults."""
+ installed_modules = neutralize.get_installed_modules(self.cr)
+ queries = neutralize.get_neutralization_queries(installed_modules)
+ for query in queries:
+ self.cr.execute(query)
+ self.backend.invalidate_recordset()
+ self.assertFalse(self.backend.username)
+ self.assertFalse(self.backend.password)
+ self.assertFalse(self.backend.api_key)
+ self.assertFalse(self.backend.oauth2_clientid)
+ self.assertFalse(self.backend.oauth2_client_secret)
+ self.assertFalse(self.backend.oauth2_client_auth_value)