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
5 changes: 5 additions & 0 deletions auth_api_key/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ 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 you create an API key, a random key is generated automatically. You
can replace it manually, or use the Generate Random Token button if an
existing record has no key. The key field is masked by default, and the
password widget provides a button to reveal the value when needed.

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

Expand Down
25 changes: 25 additions & 0 deletions auth_api_key/models/auth_api_key.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright 2018 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

import secrets

from odoo import api, fields, models, tools
from odoo.exceptions import AccessError, ValidationError
from odoo.tools import consteq
Expand All @@ -13,6 +15,7 @@ class AuthApiKey(models.Model):
name = fields.Char(required=True)
key = fields.Char(
required=True,
Comment thread
yankinmax marked this conversation as resolved.
default=lambda self: self._generate_random_key_value(),
help="""The API key. Enter a dummy value in this field if it is
obtained from the server environment configuration.""",
)
Expand All @@ -31,6 +34,28 @@ class AuthApiKey(models.Model):

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

@api.model
def _generate_random_key_value(self):
"""Return a random API key value.

The token is generated by the Odoo server instance so XML data and the
UI button can create a secret without storing any default value in the
module sources.
"""
return secrets.token_urlsafe(32)

def generate_random_key(self, api_key_ids=None):
"""Generate a key for records that do not have one yet.

:param list api_key_ids: optional record IDs, mainly used by XML data
function calls where the method is invoked on the model.
:return: True when the operation completed.
"""
api_keys = self.browse(api_key_ids) if api_key_ids else self
for api_key in api_keys.filtered(lambda record: not record.key):
api_key.key = api_key._generate_random_key_value()
return True

@api.model
def _retrieve_api_key(self, key):
return self.browse(self._retrieve_api_key_id(key))
Expand Down
5 changes: 5 additions & 0 deletions auth_api_key/readme/CONFIGURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,10 @@ 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 you create an API key, a random key is generated automatically. You
can replace it manually, or use the Generate Random Token button if an
existing record has no key. The key field is masked by default, and the
password widget provides a button to reveal the value when needed.

If you want to manage them via serve environment settings use
auth_api_key_server_env.
4 changes: 4 additions & 0 deletions auth_api_key/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,10 @@ <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 you create an API key, a random key is generated automatically. You
can replace it manually, or use the Generate Random Token button if an
existing record has no key. The key field is masked by default, and the
password widget provides a button to reveal the value when needed.</p>
<p>If you want to manage them via serve environment settings use
auth_api_key_server_env.</p>
</div>
Expand Down
19 changes: 19 additions & 0 deletions auth_api_key/tests/test_auth_api_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,25 @@ def setUpClass(cls, *args, **kwargs):
{"name": "good", "user_id": cls.demo_user.id, "key": "api_key"}
)

def test_create_without_key_generates_random_key(self):
"""Test API key creation generates an API key when no key is provided."""
api_key = self.AuthApiKey.create(
{"name": "generated", "user_id": self.demo_user.id}
)
self.assertTrue(api_key.key)
self.assertEqual(
self.AuthApiKey._retrieve_uid_from_api_key(api_key.key),
self.demo_user.id,
)

def test_generate_random_key_keeps_existing_key(self):
"""Test random key generation does not overwrite an existing key."""
api_key = self.AuthApiKey.create(
{"name": "existing", "user_id": self.demo_user.id, "key": "existing_key"}
)
api_key.generate_random_key()
self.assertEqual(api_key.key, "existing_key")

def test_lookup_key_from_db(self):
demo_user = self.demo_user
self.assertEqual(
Expand Down
13 changes: 12 additions & 1 deletion auth_api_key/views/auth_api_key.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,18 @@
</h1>
<group name="config" colspan="4" col="4">
<field name="user_id" colspan="4" />
<field name="key" colspan="4" password="True" />
<label for="key" />
<div class="o_row" colspan="3">
<!-- The password widget masks the key and provides a reveal button. -->
<field name="key" widget="password" required="1" />
<button
name="generate_random_key"
string="Generate Random Token"
type="object"
class="btn-secondary"
invisible="key"
/>
</div>
</group>
</sheet>
</form>
Expand Down
Loading