diff --git a/endpoint_auth_api_key/README.rst b/endpoint_auth_api_key/README.rst new file mode 100644 index 00000000..e920a20b --- /dev/null +++ b/endpoint_auth_api_key/README.rst @@ -0,0 +1,99 @@ +===================== +Endpoint Auth API key +===================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:b3eb2a2fa2f116c150a93e188a2ee31d785ec793407301ecb71c55dd791339f4 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb--api-lightgray.png?logo=github + :target: https://github.com/OCA/web-api/tree/17.0/endpoint_auth_api_key + :alt: OCA/web-api +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/web-api-17-0/web-api-17-0-endpoint_auth_api_key + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/web-api&target_branch=17.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Provide API key auth for endpoints. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +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 +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Camptocamp + +Contributors +------------ + +- Simone Orsi + +- `Trobz `__: + + - Son Ho + +Other credits +------------- + +The migration of this module from 14.0 to 16.0 was financially supported +by Camptocamp + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-simahawk| image:: https://github.com/simahawk.png?size=40px + :target: https://github.com/simahawk + :alt: simahawk + +Current `maintainer `__: + +|maintainer-simahawk| + +This module is part of the `OCA/web-api `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/endpoint_auth_api_key/__init__.py b/endpoint_auth_api_key/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/endpoint_auth_api_key/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/endpoint_auth_api_key/__manifest__.py b/endpoint_auth_api_key/__manifest__.py new file mode 100644 index 00000000..171a64fa --- /dev/null +++ b/endpoint_auth_api_key/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2021 Camptocamp SA +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +{ + "name": "Endpoint Auth API key", + "summary": """Provide API key auth for endpoints.""", + "version": "17.0.1.0.0", + "license": "LGPL-3", + "development_status": "Alpha", + "author": "Camptocamp, Odoo Community Association (OCA)", + "maintainers": ["simahawk"], + "website": "https://github.com/OCA/web-api", + "depends": ["endpoint", "auth_api_key_group"], + "demo": ["demo/api_key_demo.xml", "demo/endpoint_demo.xml"], + "data": ["views/endpoint_view.xml"], +} diff --git a/endpoint_auth_api_key/demo/api_key_demo.xml b/endpoint_auth_api_key/demo/api_key_demo.xml new file mode 100644 index 00000000..ded71ff2 --- /dev/null +++ b/endpoint_auth_api_key/demo/api_key_demo.xml @@ -0,0 +1,22 @@ + + + + + Endpoint API key demo + cZ6dF2UQwNcm + + + + + Endpoint API key demo 2 + kV47QyOTC5mS + + + + + Demo Group 1 + demo_group1 + + + + diff --git a/endpoint_auth_api_key/demo/endpoint_demo.xml b/endpoint_auth_api_key/demo/endpoint_demo.xml new file mode 100644 index 00000000..7063d2cc --- /dev/null +++ b/endpoint_auth_api_key/demo/endpoint_demo.xml @@ -0,0 +1,19 @@ + + + + + Demo Endpoint - auth api key + /demo/api/key + GET + api_key + + code + +result = {"response": Response("ok")} + + + + diff --git a/endpoint_auth_api_key/i18n/endpoint_auth_api_key.pot b/endpoint_auth_api_key/i18n/endpoint_auth_api_key.pot new file mode 100644 index 00000000..9942bb16 --- /dev/null +++ b/endpoint_auth_api_key/i18n/endpoint_auth_api_key.pot @@ -0,0 +1,30 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * endpoint_auth_api_key +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: endpoint_auth_api_key +#: model:ir.model.fields,field_description:endpoint_auth_api_key.field_endpoint_endpoint__auth_api_key_group_ids +#: model:ir.model.fields,field_description:endpoint_auth_api_key.field_endpoint_mixin__auth_api_key_group_ids +msgid "Allowed API key groups" +msgstr "" + +#. module: endpoint_auth_api_key +#: model:ir.model,name:endpoint_auth_api_key.model_endpoint_mixin +msgid "Endpoint mixin" +msgstr "" + +#. module: endpoint_auth_api_key +#: model_terms:ir.ui.view,arch_db:endpoint_auth_api_key.endpoint_mixin_form_view +msgid "Manage groups" +msgstr "" diff --git a/endpoint_auth_api_key/i18n/it.po b/endpoint_auth_api_key/i18n/it.po new file mode 100644 index 00000000..2a9fea0b --- /dev/null +++ b/endpoint_auth_api_key/i18n/it.po @@ -0,0 +1,33 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * endpoint_auth_api_key +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-04-02 12:35+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: endpoint_auth_api_key +#: model:ir.model.fields,field_description:endpoint_auth_api_key.field_endpoint_endpoint__auth_api_key_group_ids +#: model:ir.model.fields,field_description:endpoint_auth_api_key.field_endpoint_mixin__auth_api_key_group_ids +msgid "Allowed API key groups" +msgstr "Consenti gruppi chiavi API" + +#. module: endpoint_auth_api_key +#: model:ir.model,name:endpoint_auth_api_key.model_endpoint_mixin +msgid "Endpoint mixin" +msgstr "Mixin endpoint" + +#. module: endpoint_auth_api_key +#: model_terms:ir.ui.view,arch_db:endpoint_auth_api_key.endpoint_mixin_form_view +msgid "Manage groups" +msgstr "Gestione gruppi" diff --git a/endpoint_auth_api_key/models/__init__.py b/endpoint_auth_api_key/models/__init__.py new file mode 100644 index 00000000..95c62dfe --- /dev/null +++ b/endpoint_auth_api_key/models/__init__.py @@ -0,0 +1 @@ +from . import endpoint_mixin diff --git a/endpoint_auth_api_key/models/endpoint_mixin.py b/endpoint_auth_api_key/models/endpoint_mixin.py new file mode 100644 index 00000000..613d2079 --- /dev/null +++ b/endpoint_auth_api_key/models/endpoint_mixin.py @@ -0,0 +1,40 @@ +# Copyright 2021 Camptocamp SA +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import werkzeug + +from odoo import fields, models + + +class EndpointMixin(models.AbstractModel): + _inherit = "endpoint.mixin" + + auth_api_key_group_ids = fields.Many2many( + comodel_name="auth.api.key.group", + string="Allowed API key groups", + ) + + def _selection_auth_type(self): + return super()._selection_auth_type() + [("api_key", "API key")] + + def _validate_request(self, request): + super()._validate_request(request) + if self.auth_type == "api_key": + self._validate_api_key(request) + return + + def _validate_api_key(self, request): + key_id = request.auth_api_key_id + if key_id not in self._allowed_api_key_ids(): + self._logger.error("API key %s not allowed on %s", key_id, self.route) + raise werkzeug.exceptions.Forbidden() + + def _allowed_api_key_ids(self): + return self.auth_api_key_group_ids.auth_api_key_ids.ids + + def action_view_api_key_groups(self): + xmlid = "auth_api_key_group.auth_api_key_group_act_window" + action = self.env["ir.actions.act_window"]._for_xml_id(xmlid) + action["domain"] = [("id", "in", self.auth_api_key_group_ids.ids)] + return action diff --git a/endpoint_auth_api_key/pyproject.toml b/endpoint_auth_api_key/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/endpoint_auth_api_key/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/endpoint_auth_api_key/readme/CONTRIBUTORS.md b/endpoint_auth_api_key/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..f2fa2f75 --- /dev/null +++ b/endpoint_auth_api_key/readme/CONTRIBUTORS.md @@ -0,0 +1,5 @@ +- Simone Orsi \<\> + +- [Trobz](https://trobz.com): + + > - Son Ho \<\> diff --git a/endpoint_auth_api_key/readme/CREDITS.md b/endpoint_auth_api_key/readme/CREDITS.md new file mode 100644 index 00000000..44a62335 --- /dev/null +++ b/endpoint_auth_api_key/readme/CREDITS.md @@ -0,0 +1,2 @@ +The migration of this module from 14.0 to 16.0 was financially supported +by Camptocamp diff --git a/endpoint_auth_api_key/readme/DESCRIPTION.md b/endpoint_auth_api_key/readme/DESCRIPTION.md new file mode 100644 index 00000000..7d9743ba --- /dev/null +++ b/endpoint_auth_api_key/readme/DESCRIPTION.md @@ -0,0 +1 @@ +Provide API key auth for endpoints. diff --git a/endpoint_auth_api_key/static/description/icon.png b/endpoint_auth_api_key/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/endpoint_auth_api_key/static/description/icon.png differ diff --git a/endpoint_auth_api_key/static/description/index.html b/endpoint_auth_api_key/static/description/index.html new file mode 100644 index 00000000..1f1cf255 --- /dev/null +++ b/endpoint_auth_api_key/static/description/index.html @@ -0,0 +1,445 @@ + + + + + +Endpoint Auth API key + + + +
+

Endpoint Auth API key

+ + +

Alpha License: LGPL-3 OCA/web-api Translate me on Weblate Try me on Runboat

+

Provide API key auth for endpoints.

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

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 +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Camptocamp
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The migration of this module from 14.0 to 16.0 was financially supported +by Camptocamp

+
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

simahawk

+

This module is part of the OCA/web-api project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/endpoint_auth_api_key/tests/__init__.py b/endpoint_auth_api_key/tests/__init__.py new file mode 100644 index 00000000..6885a0f9 --- /dev/null +++ b/endpoint_auth_api_key/tests/__init__.py @@ -0,0 +1,2 @@ +from . import test_endpoint +from . import test_endpoint_controller diff --git a/endpoint_auth_api_key/tests/test_endpoint.py b/endpoint_auth_api_key/tests/test_endpoint.py new file mode 100644 index 00000000..a9c0009c --- /dev/null +++ b/endpoint_auth_api_key/tests/test_endpoint.py @@ -0,0 +1,63 @@ +# Copyright 2021 Camptocamp SA +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + + +import werkzeug + +from odoo.tools.misc import mute_logger + +from odoo.addons.endpoint.tests.common import CommonEndpoint + + +class TestEndpoint(CommonEndpoint): + @classmethod + def _setup_records(cls): + super()._setup_records() + cls.endpoint = cls.env.ref("endpoint_auth_api_key.endpoint_demo_1") + cls.key_group = cls.env.ref("endpoint_auth_api_key.auth_api_key_group_demo") + cls.api_key = cls.env.ref("endpoint_auth_api_key.auth_api_key_demo") + cls.api_key2 = cls.env.ref("endpoint_auth_api_key.auth_api_key_demo2") + return + + @mute_logger("endpoint.endpoint") + def test_endpoint_validate_request_no_key(self): + endpoint = self.endpoint.copy( + { + "route": "/api-key-test", + "request_method": "GET", + } + ) + with self.assertRaises(werkzeug.exceptions.Forbidden): + with self._get_mocked_request( + httprequest={"method": "GET"}, + ) as req: + endpoint._validate_request(req) + + @mute_logger("endpoint.endpoint") + def test_endpoint_validate_request_bad_key(self): + endpoint = self.endpoint.copy( + { + "route": "/api-key-test", + "request_method": "GET", + } + ) + with self.assertRaises(werkzeug.exceptions.Forbidden): + with self._get_mocked_request( + httprequest={"method": "GET"}, + request_attrs={"auth_api_key_id": self.api_key2.id}, + ) as req: + endpoint._validate_request(req) + + def test_endpoint_validate_request_good_key(self): + endpoint = self.endpoint.copy( + { + "route": "/api-key-test", + "request_method": "GET", + } + ) + with self._get_mocked_request( + httprequest={"method": "GET"}, + request_attrs={"auth_api_key_id": self.api_key.id}, + ) as req: + endpoint._validate_request(req) diff --git a/endpoint_auth_api_key/tests/test_endpoint_controller.py b/endpoint_auth_api_key/tests/test_endpoint_controller.py new file mode 100644 index 00000000..fe6a2f65 --- /dev/null +++ b/endpoint_auth_api_key/tests/test_endpoint_controller.py @@ -0,0 +1,56 @@ +# Copyright 2021 Camptocamp SA +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import os +import unittest + +import requests + +from odoo import tools +from odoo.tests.common import HttpCase +from odoo.tools.misc import mute_logger + + +@unittest.skipIf(os.getenv("SKIP_HTTP_CASE"), "EndpoinAuthApikeytHttpCase skipped") +class EndpoinAuthApikeytHttpCase(HttpCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.api_key = cls.env.ref("endpoint_auth_api_key.auth_api_key_demo") + cls.api_key2 = cls.env.ref("endpoint_auth_api_key.auth_api_key_demo2") + # force sync for demo records + cls.env["endpoint.endpoint"].search([])._handle_registry_sync() + + def tearDown(self): + # Clear cache for method ``ir.http.routing_map()`` + self.env.registry.clear_cache("routing") + super().tearDown() + + def _make_url(self, route): + return f"http://127.0.0.1:{tools.config['http_port']}{route}" + + def _make_request(self, route, api_key=None, headers=None): + # use requests because you cannot easily manipulate the request w/ `url_open` + headers = headers or {} + if api_key: + headers.update({"API-KEY": api_key.key}) + return requests.get(self._make_url(route), headers=headers, timeout=60) + + @mute_logger("odoo.addons.auth_api_key.models.ir_http") + def test_call_no_key(self): + route = "/demo/api/key" + response = self._make_request(route) + self.assertEqual(response.status_code, 403) + + def test_call_good_key(self): + route = "/demo/api/key" + response = self._make_request(route, api_key=self.api_key) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, b"ok") + + @mute_logger("endpoint.endpoint") + def test_call_bad_key(self): + route = "/demo/api/key" + response = self._make_request(route, api_key=self.api_key2) + self.assertEqual(response.status_code, 403) diff --git a/endpoint_auth_api_key/views/endpoint_view.xml b/endpoint_auth_api_key/views/endpoint_view.xml new file mode 100644 index 00000000..4023996a --- /dev/null +++ b/endpoint_auth_api_key/views/endpoint_view.xml @@ -0,0 +1,27 @@ + + + + + + endpoint.mixin + + + + +