Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions auth_api_key/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ The api key menu is available into Settings > Technical in debug mode.
By default, when you create an API key, the key is saved into the
database.

When a database is neutralized, stored API key values are cleared.

If you want to manage them via serve environment settings use
auth_api_key_server_env.

Expand Down
3 changes: 3 additions & 0 deletions auth_api_key/data/neutralize.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- remove API keys
UPDATE auth_api_key
SET key = NULL;
9 changes: 7 additions & 2 deletions auth_api_key/models/auth_api_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ class AuthApiKey(models.Model):

name = fields.Char(required=True)
key = fields.Char(
required=True,
help="""The API key. Enter a dummy value in this field if it is
obtained from the server environment configuration.""",
)
Expand All @@ -31,6 +30,12 @@ class AuthApiKey(models.Model):

_name_uniq = models.Constraint("unique(name)", "Api Key name must be unique.")

@api.constrains("key")
def _check_key_required(self):
for api_key in self:
if not api_key.key:
raise ValidationError(self.env._("The API key is required."))

@api.model
def _retrieve_api_key(self, key):
return self.browse(self._retrieve_api_key_id(key))
Expand All @@ -40,7 +45,7 @@ def _retrieve_api_key(self, key):
def _retrieve_api_key_id(self, key):
if not self.env.user.has_group("base.group_system"):
raise AccessError(self.env._("User is not allowed"))
for api_key in self.search([], limit=None):
for api_key in self.search([("key", "!=", False)], limit=None):
if api_key.key and consteq(key, api_key.key):
return api_key.id
raise ValidationError(self.env._("The key '%s' is not allowed", key))
Expand Down
2 changes: 2 additions & 0 deletions auth_api_key/readme/CONFIGURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ The api key menu is available into Settings \> Technical in debug mode.
By default, when you create an API key, the key is saved into the
database.

When a database is neutralized, stored API key values are cleared.

If you want to manage them via serve environment settings use
auth_api_key_server_env.
1 change: 1 addition & 0 deletions auth_api_key/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ <h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
<p>The api key menu is available into Settings &gt; Technical in debug mode.
By default, when you create an API key, the key is saved into the
database.</p>
<p>When a database is neutralized, stored API key values are cleared.</p>
<p>If you want to manage them via serve environment settings use
auth_api_key_server_env.</p>
</div>
Expand Down
1 change: 1 addition & 0 deletions auth_api_key/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from . import test_auth_api_key
from . import test_controllers
from . import test_neutralize
8 changes: 8 additions & 0 deletions auth_api_key/tests/test_auth_api_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ def test_wrong_key(self):
with self.assertRaises(ValidationError), self.env.cr.savepoint():
self.env["auth.api.key"]._retrieve_uid_from_api_key("api_wrong_key")

def test_empty_key_is_rejected(self):
with self.assertRaises(ValidationError), self.env.cr.savepoint():
self.AuthApiKey.create(
{"name": "empty", "user_id": self.demo_user.id, "key": ""}
)
with self.assertRaises(ValidationError), self.env.cr.savepoint():
self.api_key_good.key = False

def test_user_not_allowed(self):
# only system users can check for key
with self.assertRaises(AccessError), self.env.cr.savepoint():
Expand Down
25 changes: 25 additions & 0 deletions auth_api_key/tests/test_neutralize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2026 Camptocamp SA
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from odoo.modules import neutralize
from odoo.tests import tagged
from odoo.tests.common import TransactionCase


@tagged("post_install", "-at_install", "neutralize")
class TestAuthApiKeyNeutralize(TransactionCase):
def test_neutralize_removes_api_key_values(self):
"""Test database neutralization clears stored API key secrets."""
api_key = self.env["auth.api.key"].create(
{
"name": "neutralize",
"user_id": self.env.ref("base.user_admin").id,
"key": "secret-key",
}
)

queries = neutralize.get_neutralization_queries(["auth_api_key"])
for query in queries:
self.cr.execute(query)
api_key.invalidate_recordset(["key"])
self.assertFalse(api_key.key)
Loading