From 7395a136b8e8869873d5cb75dfdb64cdb1f11ae8 Mon Sep 17 00:00:00 2001 From: Florian Mounier Date: Mon, 2 Jun 2025 12:56:59 +0200 Subject: [PATCH 1/9] [ADD] auth_partner, fastapi_auth_partner --- fastapi_auth_partner/README.rst | 136 +++++ fastapi_auth_partner/__init__.py | 4 + fastapi_auth_partner/__manifest__.py | 32 ++ .../demo/fastapi_endpoint_demo.xml | 26 + fastapi_auth_partner/dependencies.py | 74 +++ fastapi_auth_partner/models/__init__.py | 3 + fastapi_auth_partner/models/auth_directory.py | 51 ++ fastapi_auth_partner/models/auth_partner.py | 82 +++ .../models/fastapi_endpoint.py | 55 ++ fastapi_auth_partner/readme/CONTRIBUTORS.rst | 4 + fastapi_auth_partner/readme/DESCRIPTION.rst | 2 + fastapi_auth_partner/readme/USAGE.rst | 52 ++ fastapi_auth_partner/routers/__init__.py | 1 + fastapi_auth_partner/routers/auth.py | 252 +++++++++ fastapi_auth_partner/schemas.py | 40 ++ .../security/ir.model.access.csv | 2 + fastapi_auth_partner/security/res_group.xml | 13 + .../static/description/index.html | 477 ++++++++++++++++++ fastapi_auth_partner/tests/__init__.py | 2 + fastapi_auth_partner/tests/test_auth.py | 243 +++++++++ .../tests/test_fastapi_auth_partner_demo.py | 93 ++++ .../views/auth_directory_view.xml | 29 ++ .../views/auth_partner_view.xml | 31 ++ .../views/fastapi_endpoint_view.xml | 25 + fastapi_auth_partner/wizards/__init__.py | 2 + .../wizard_auth_partner_impersonate.py | 29 ++ .../wizard_auth_partner_impersonate_view.xml | 43 ++ .../wizard_auth_partner_reset_password.py | 18 + ...izard_auth_partner_reset_password_view.xml | 17 + 29 files changed, 1838 insertions(+) create mode 100644 fastapi_auth_partner/README.rst create mode 100644 fastapi_auth_partner/__init__.py create mode 100644 fastapi_auth_partner/__manifest__.py create mode 100644 fastapi_auth_partner/demo/fastapi_endpoint_demo.xml create mode 100644 fastapi_auth_partner/dependencies.py create mode 100644 fastapi_auth_partner/models/__init__.py create mode 100644 fastapi_auth_partner/models/auth_directory.py create mode 100644 fastapi_auth_partner/models/auth_partner.py create mode 100644 fastapi_auth_partner/models/fastapi_endpoint.py create mode 100644 fastapi_auth_partner/readme/CONTRIBUTORS.rst create mode 100644 fastapi_auth_partner/readme/DESCRIPTION.rst create mode 100644 fastapi_auth_partner/readme/USAGE.rst create mode 100644 fastapi_auth_partner/routers/__init__.py create mode 100644 fastapi_auth_partner/routers/auth.py create mode 100644 fastapi_auth_partner/schemas.py create mode 100644 fastapi_auth_partner/security/ir.model.access.csv create mode 100644 fastapi_auth_partner/security/res_group.xml create mode 100644 fastapi_auth_partner/static/description/index.html create mode 100644 fastapi_auth_partner/tests/__init__.py create mode 100644 fastapi_auth_partner/tests/test_auth.py create mode 100644 fastapi_auth_partner/tests/test_fastapi_auth_partner_demo.py create mode 100644 fastapi_auth_partner/views/auth_directory_view.xml create mode 100644 fastapi_auth_partner/views/auth_partner_view.xml create mode 100644 fastapi_auth_partner/views/fastapi_endpoint_view.xml create mode 100644 fastapi_auth_partner/wizards/__init__.py create mode 100644 fastapi_auth_partner/wizards/wizard_auth_partner_impersonate.py create mode 100644 fastapi_auth_partner/wizards/wizard_auth_partner_impersonate_view.xml create mode 100644 fastapi_auth_partner/wizards/wizard_auth_partner_reset_password.py create mode 100644 fastapi_auth_partner/wizards/wizard_auth_partner_reset_password_view.xml diff --git a/fastapi_auth_partner/README.rst b/fastapi_auth_partner/README.rst new file mode 100644 index 000000000..69e036674 --- /dev/null +++ b/fastapi_auth_partner/README.rst @@ -0,0 +1,136 @@ +==================== +Fastapi Auth Partner +==================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:57e60d4a203cd5e613e07fe0c1f6a207bd6b77f537ed5cbbbe72a1cc2184f0de + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Frest--framework-lightgray.png?logo=github + :target: https://github.com/OCA/rest-framework/tree/16.0/fastapi_auth_partner + :alt: OCA/rest-framework +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/rest-framework-16-0/rest-framework-16-0-fastapi_auth_partner + :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/rest-framework&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module is the FastAPI implementation of `auth_partner <../auth_partner>`_ +it provides all the routes to manage the authentication of partners. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +First you have to add the auth router to your FastAPI endpoint and the authentication dependency to your app dependencies: + +.. code-block:: python + + from odoo.addons.fastapi import dependencies + from odoo.addons.fastapi_auth_partner.dependencies import ( + auth_partner_authenticated_partner, + ) + from odoo.addons.fastapi_auth_partner.routers.auth import auth_router + + class FastapiEndpoint(models.Model): + _inherit = "fastapi.endpoint" + + def _get_fastapi_routers(self): + if self.app == "myapp": + return [ + auth_router, + ] + return super()._get_fastapi_routers() + + def _get_app_dependencies_overrides(self): + res = super()._get_app_dependencies_overrides() + if self.app == "myapp": + res.update( + { + dependencies.authenticated_partner_impl: auth_partner_authenticated_partner, + } + ) + return res + +Next you can manage your authenticable partners and directories in the Odoo interface: + +FastAPI > Authentication > Partner + +and + +FastAPI > Authentication > Directory + +Next you must set the directory used for the authentication in the FastAPI endpoint: + +FastAPI > FastAPI Endpoint > myapp > Directory + +Then you can use the auth router to authenticate your requests: + +- POST /auth/register to register a partner +- POST /auth/login to authenticate a partner +- POST /auth/logout to unauthenticate a partner +- POST /auth/validate_email to validate a partner email +- POST /auth/request_reset_password to request a password reset +- POST /auth/set_password to set a new password +- GET /auth/profile to get the partner profile +- GET /auth/impersonate to impersonate a partner + +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 +~~~~~~~ + +* Akretion + +Contributors +~~~~~~~~~~~~ + +* `Akretion `_: + + * Sébastien Beau + * Florian Mounier + +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. + +This module is part of the `OCA/rest-framework `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fastapi_auth_partner/__init__.py b/fastapi_auth_partner/__init__.py new file mode 100644 index 000000000..3f274f8d1 --- /dev/null +++ b/fastapi_auth_partner/__init__.py @@ -0,0 +1,4 @@ +from . import models +from . import routers +from . import schemas +from . import wizards diff --git a/fastapi_auth_partner/__manifest__.py b/fastapi_auth_partner/__manifest__.py new file mode 100644 index 000000000..e5b486e4c --- /dev/null +++ b/fastapi_auth_partner/__manifest__.py @@ -0,0 +1,32 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Fastapi Auth Partner", + "summary": """This provides an implementation of auth_partner for FastAPI""", + "version": "16.0.1.0.0", + "license": "AGPL-3", + "author": "Akretion,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/rest-framework", + "depends": [ + "extendable_fastapi", + "auth_partner", + ], + "data": [ + "security/res_group.xml", + "security/ir.model.access.csv", + "views/auth_partner_view.xml", + "views/auth_directory_view.xml", + "views/fastapi_endpoint_view.xml", + "wizards/wizard_auth_partner_impersonate_view.xml", + "wizards/wizard_auth_partner_reset_password_view.xml", + ], + "demo": [ + "demo/fastapi_endpoint_demo.xml", + ], + "external_dependencies": { + "python": ["itsdangerous"], + }, +} diff --git a/fastapi_auth_partner/demo/fastapi_endpoint_demo.xml b/fastapi_auth_partner/demo/fastapi_endpoint_demo.xml new file mode 100644 index 000000000..bf017151c --- /dev/null +++ b/fastapi_auth_partner/demo/fastapi_endpoint_demo.xml @@ -0,0 +1,26 @@ + + + + Fastapi Auth Partner Demo Endpoint + + demo + /fastapi_auth_partner_demo + auth_partner + + + https://api.example.com/ + https://www.example.com/ + + + + + + diff --git a/fastapi_auth_partner/dependencies.py b/fastapi_auth_partner/dependencies.py new file mode 100644 index 000000000..5d336e59c --- /dev/null +++ b/fastapi_auth_partner/dependencies.py @@ -0,0 +1,74 @@ +# Copyright 2024 Akretion (https://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import logging +import sys +from typing import Any, Dict, Union + +from itsdangerous import URLSafeTimedSerializer +from starlette.status import HTTP_401_UNAUTHORIZED + +from odoo.api import Environment + +from odoo.addons.base.models.res_partner import Partner +from odoo.addons.fastapi.dependencies import fastapi_endpoint, odoo_env +from odoo.addons.fastapi.models import FastapiEndpoint + +from fastapi import Cookie, Depends, HTTPException, Request, Response + +if sys.version_info >= (3, 9): + from typing import Annotated +else: + from typing_extensions import Annotated + +_logger = logging.getLogger(__name__) + + +Payload = Dict[str, Any] + + +class AuthPartner: + def __init__(self, allow_unauthenticated: bool = False): + self.allow_unauthenticated = allow_unauthenticated + + def __call__( + self, + request: Request, + response: Response, + env: Annotated[ + Environment, + Depends(odoo_env), + ], + endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)], + fastapi_auth_partner: Annotated[Union[str, None], Cookie()] = None, + ) -> Partner: + if not fastapi_auth_partner and self.allow_unauthenticated: + return env["res.partner"].with_user(env.ref("base.public_user")).browse() + + elif fastapi_auth_partner: + directory = endpoint.sudo().directory_id + try: + vals = URLSafeTimedSerializer( + directory.cookie_secret_key or directory.secret_key + ).loads(fastapi_auth_partner, max_age=directory.cookie_duration * 60) + except Exception as e: + _logger.error("Invalid cookies error %s", e) + raise HTTPException(status_code=HTTP_401_UNAUTHORIZED) from e + if vals["did"] == directory.id and vals["pid"]: + partner = env["res.partner"].browse(vals["pid"]).exists() + if partner: + auth_partner = partner._get_auth_partner_for_directory(directory) + if auth_partner: + if directory.sliding_session: + helper = env["fastapi.auth.service"].new( + {"endpoint_id": endpoint} + ) + helper._set_auth_cookie(auth_partner, request, response) + return partner + _logger.info("Could not determine partner from 'fastapi_auth_partner' cookie.") + raise HTTPException(status_code=HTTP_401_UNAUTHORIZED) + + +auth_partner_authenticated_partner = AuthPartner() +auth_partner_optionally_authenticated_partner = AuthPartner(allow_unauthenticated=True) diff --git a/fastapi_auth_partner/models/__init__.py b/fastapi_auth_partner/models/__init__.py new file mode 100644 index 000000000..526f7a263 --- /dev/null +++ b/fastapi_auth_partner/models/__init__.py @@ -0,0 +1,3 @@ +from . import auth_directory +from . import auth_partner +from . import fastapi_endpoint diff --git a/fastapi_auth_partner/models/auth_directory.py b/fastapi_auth_partner/models/auth_directory.py new file mode 100644 index 000000000..b671bf96d --- /dev/null +++ b/fastapi_auth_partner/models/auth_directory.py @@ -0,0 +1,51 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import fields, models + + +class AuthDirectory(models.Model): + _inherit = "auth.directory" + + fastapi_endpoint_ids = fields.One2many( + "fastapi.endpoint", + "directory_id", + string="FastAPI Endpoints", + ) + + cookie_secret_key = fields.Char( + groups="base.group_system", + help="The secret key used to sign the cookie", + required=True, + default=lambda self: self._generate_default_secret_key(), + ) + cookie_duration = fields.Integer( + default=525600, + help="In minute, default 525600 minutes => 1 year", + required=True, + ) + sliding_session = fields.Boolean() + + def action_regenerate_cookie_secret_key(self): + self.ensure_one() + self.cookie_secret_key = self._generate_default_secret_key() + + def _prepare_mail_context(self, context): + rv = super()._prepare_mail_context(context) + endpoint_id = self.env.context.get("_fastapi_endpoint_id") + + if endpoint_id: + endpoint = self.env["fastapi.endpoint"].browse(endpoint_id) + rv["public_url"] = endpoint.public_url or endpoint.public_api_url + + return rv + + @property + def _server_env_fields(self): + return { + **super()._server_env_fields, + "cookie_secret_key": {}, + } diff --git a/fastapi_auth_partner/models/auth_partner.py b/fastapi_auth_partner/models/auth_partner.py new file mode 100644 index 000000000..5cba6581b --- /dev/null +++ b/fastapi_auth_partner/models/auth_partner.py @@ -0,0 +1,82 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, models +from odoo.exceptions import AccessDenied, UserError +from odoo.http import request + + +class AuthPartner(models.Model): + _inherit = "auth.partner" + + def local_impersonate(self): + """Local impersonate for dev mode""" + self.ensure_one() + + if not self.env.user._is_admin(): + raise AccessDenied(_("Only admin can impersonate locally")) + + if not hasattr(request, "future_response"): + raise UserError( + _("Please install base_future_response for local impersonate to work") + ) + + for endpoint in self.directory_id.fastapi_endpoint_ids: + helper = self.env["fastapi.auth.service"].new({"endpoint_id": endpoint}) + helper._set_auth_cookie(self, request.httprequest, request.future_response) + + return { + "type": "ir.actions.client", + "tag": "display_notification", + "params": { + "title": _("Impersonation successful"), + "message": _("You are now impersonating %s\n%%s") % self.login, + "links": [ + { + "label": f"{endpoint.app.title()} api docs", + "url": endpoint.docs_url, + } + for endpoint in self.directory_id.fastapi_endpoint_ids + ], + "type": "success", + "sticky": False, + }, + } + + def _get_impersonate_url(self, token, **kwargs): + endpoint = kwargs.get("endpoint") + if not endpoint: + return super()._get_impersonate_url(token, **kwargs) + + base = ( + endpoint.public_api_url + or endpoint.public_url + or ( + self.env["ir.config_parameter"].sudo().get_param("web.base.url") + + endpoint.root_path + ) + ) + return f"{base.rstrip('/')}/auth/impersonate/{token}" + + def _get_impersonate_action(self, token, **kwargs): + # Get the endpoint from a wizard + endpoint_id = self.env.context.get("fastapi_endpoint_id") + endpoint = None + + if endpoint_id: + endpoint = self.env["fastapi.endpoint"].browse(endpoint_id) + + if not endpoint: + endpoints = self.directory_id.fastapi_endpoint_ids + if len(endpoints) == 1: + endpoint = endpoints + else: + wizard = self.env["ir.actions.act_window"]._for_xml_id( + "fastapi_auth_partner.auth_partner_action_impersonate" + ) + wizard["context"] = {"default_auth_partner_id": self.id} + return wizard + + return super()._get_impersonate_action(token, endpoint=endpoint, **kwargs) diff --git a/fastapi_auth_partner/models/fastapi_endpoint.py b/fastapi_auth_partner/models/fastapi_endpoint.py new file mode 100644 index 000000000..cab4f80c5 --- /dev/null +++ b/fastapi_auth_partner/models/fastapi_endpoint.py @@ -0,0 +1,55 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from typing import List + +from odoo import fields, models + +from fastapi import APIRouter + +from ..routers.auth import auth_router + + +class FastapiEndpoint(models.Model): + _inherit = "fastapi.endpoint" + + app = fields.Selection( + selection_add=[("demo", "Demo Endpoint")], ondelete={"demo": "cascade"} + ) + demo_auth_method = fields.Selection( + selection_add=[ + ("auth_partner", "Partner Auth"), + ], + string="Authentication method", + ) + directory_id = fields.Many2one("auth.directory") + + is_auth_partner = fields.Boolean( + compute="_compute_is_auth_partner", + help="Technical field to know if the auth method is partner", + ) + public_api_url: str = fields.Char( + help="The public URL of the API.\n" + "This URL is used in impersonation to set the cookie on the right API " + "domain if you use a reverse proxy to serve the API.\n" + "Defaults to the public_url if not set or the odoo url if not set either." + ) + # More info in https://github.com/OCA/rest-framework/pull/438/files + public_url: str = fields.Char( + help="The public URL of the site.\n" + "This URL is used for the impersonation final redirect. " + "And can also be used in the mail template to construct links.\n" + "Default to the public_api_url if not set or the odoo url if not set either." + ) + + def _get_fastapi_routers(self) -> List[APIRouter]: + routers = super()._get_fastapi_routers() + if self.app == "demo" and self.demo_auth_method == "auth_partner": + routers.append(auth_router) + return routers + + def _compute_is_auth_partner(self): + for rec in self: + rec.is_auth_partner = auth_router in rec._get_fastapi_routers() diff --git a/fastapi_auth_partner/readme/CONTRIBUTORS.rst b/fastapi_auth_partner/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..bae3cc9a1 --- /dev/null +++ b/fastapi_auth_partner/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* `Akretion `_: + + * Sébastien Beau + * Florian Mounier diff --git a/fastapi_auth_partner/readme/DESCRIPTION.rst b/fastapi_auth_partner/readme/DESCRIPTION.rst new file mode 100644 index 000000000..e2fa8ca8d --- /dev/null +++ b/fastapi_auth_partner/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module is the FastAPI implementation of `auth_partner <../auth_partner>`_ +it provides all the routes to manage the authentication of partners. diff --git a/fastapi_auth_partner/readme/USAGE.rst b/fastapi_auth_partner/readme/USAGE.rst new file mode 100644 index 000000000..316655690 --- /dev/null +++ b/fastapi_auth_partner/readme/USAGE.rst @@ -0,0 +1,52 @@ +First you have to add the auth router to your FastAPI endpoint and the authentication dependency to your app dependencies: + +.. code-block:: python + + from odoo.addons.fastapi import dependencies + from odoo.addons.fastapi_auth_partner.dependencies import ( + auth_partner_authenticated_partner, + ) + from odoo.addons.fastapi_auth_partner.routers.auth import auth_router + + class FastapiEndpoint(models.Model): + _inherit = "fastapi.endpoint" + + def _get_fastapi_routers(self): + if self.app == "myapp": + return [ + auth_router, + ] + return super()._get_fastapi_routers() + + def _get_app_dependencies_overrides(self): + res = super()._get_app_dependencies_overrides() + if self.app == "myapp": + res.update( + { + dependencies.authenticated_partner_impl: auth_partner_authenticated_partner, + } + ) + return res + +Next you can manage your authenticable partners and directories in the Odoo interface: + +FastAPI > Authentication > Partner + +and + +FastAPI > Authentication > Directory + +Next you must set the directory used for the authentication in the FastAPI endpoint: + +FastAPI > FastAPI Endpoint > myapp > Directory + +Then you can use the auth router to authenticate your requests: + +- POST /auth/register to register a partner +- POST /auth/login to authenticate a partner +- POST /auth/logout to unauthenticate a partner +- POST /auth/validate_email to validate a partner email +- POST /auth/request_reset_password to request a password reset +- POST /auth/set_password to set a new password +- GET /auth/profile to get the partner profile +- GET /auth/impersonate to impersonate a partner diff --git a/fastapi_auth_partner/routers/__init__.py b/fastapi_auth_partner/routers/__init__.py new file mode 100644 index 000000000..582cb2cd7 --- /dev/null +++ b/fastapi_auth_partner/routers/__init__.py @@ -0,0 +1 @@ +from .auth import auth_router diff --git a/fastapi_auth_partner/routers/auth.py b/fastapi_auth_partner/routers/auth.py new file mode 100644 index 000000000..b06f4fcff --- /dev/null +++ b/fastapi_auth_partner/routers/auth.py @@ -0,0 +1,252 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import sys + +if sys.version_info >= (3, 9): + from typing import Annotated +else: + from typing_extensions import Annotated + +from datetime import datetime, timedelta, timezone + +from itsdangerous import URLSafeTimedSerializer + +from odoo import _, fields, models, tools +from odoo.api import Environment +from odoo.exceptions import ValidationError + +from odoo.addons.base.models.res_partner import Partner +from odoo.addons.fastapi.dependencies import fastapi_endpoint, odoo_env +from odoo.addons.fastapi.models import FastapiEndpoint + +from fastapi import APIRouter, Depends, Request, Response +from fastapi.responses import RedirectResponse + +from ..dependencies import auth_partner_authenticated_partner +from ..schemas import ( + AuthForgetPasswordInput, + AuthLoginInput, + AuthPartnerResponse, + AuthRegisterInput, + AuthSetPasswordInput, + AuthValidateEmailInput, +) + +COOKIE_AUTH_NAME = "fastapi_auth_partner" + +auth_router = APIRouter(tags=["auth"]) + + +@auth_router.post("/auth/register", status_code=201) +def register( + data: AuthRegisterInput, + env: Annotated[Environment, Depends(odoo_env)], + endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)], + request: Request, + response: Response, +) -> AuthPartnerResponse: + helper = env["fastapi.auth.service"].new({"endpoint_id": endpoint}) + auth_partner = helper._signup(data) + helper._set_auth_cookie(auth_partner, request, response) + return AuthPartnerResponse.from_auth_partner(auth_partner) + + +@auth_router.post("/auth/login") +def login( + data: AuthLoginInput, + env: Annotated[Environment, Depends(odoo_env)], + endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)], + request: Request, + response: Response, +) -> AuthPartnerResponse: + helper = env["fastapi.auth.service"].new({"endpoint_id": endpoint}) + auth_partner = helper._login(data) + helper._set_auth_cookie(auth_partner, request, response) + return AuthPartnerResponse.from_auth_partner(auth_partner) + + +@auth_router.post("/auth/logout", status_code=205) +def logout( + env: Annotated[Environment, Depends(odoo_env)], + endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)], + response: Response, +): + helper = env["fastapi.auth.service"].new({"endpoint_id": endpoint}) + helper._logout() + helper._clear_auth_cookie(response) + return {} + + +@auth_router.post("/auth/validate_email") +def validate_email( + data: AuthValidateEmailInput, + env: Annotated[Environment, Depends(odoo_env)], + endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)], +): + helper = env["fastapi.auth.service"].new({"endpoint_id": endpoint}) + helper._validate_email(data) + return {} + + +@auth_router.post("/auth/request_reset_password") +def request_reset_password( + data: AuthForgetPasswordInput, + env: Annotated[Environment, Depends(odoo_env)], + endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)], +): + helper = env["fastapi.auth.service"].new({"endpoint_id": endpoint}) + helper._request_reset_password(data) + return {} + + +@auth_router.post("/auth/set_password") +def set_password( + data: AuthSetPasswordInput, + env: Annotated[Environment, Depends(odoo_env)], + endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)], + request: Request, + response: Response, +) -> AuthPartnerResponse: + helper = env["fastapi.auth.service"].new({"endpoint_id": endpoint}) + auth_partner = helper._set_password(data) + helper._set_auth_cookie(auth_partner, request, response) + return AuthPartnerResponse.from_auth_partner(auth_partner) + + +@auth_router.get("/auth/profile") +def profile( + env: Annotated[Environment, Depends(odoo_env)], + endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)], + partner: Annotated[Partner, Depends(auth_partner_authenticated_partner)], +) -> AuthPartnerResponse: + helper = env["fastapi.auth.service"].new({"endpoint_id": endpoint}) + auth_partner = helper._get_auth_from_partner(partner) + return AuthPartnerResponse.from_auth_partner(auth_partner) + + +@auth_router.get("/auth/impersonate/{token}") +def impersonate( + token: str, + env: Annotated[Environment, Depends(odoo_env)], + endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)], + request: Request, +) -> RedirectResponse: + helper = env["fastapi.auth.service"].new({"endpoint_id": endpoint}) + auth_partner = helper._impersonate(token) + base = ( + endpoint.public_url + or endpoint.public_api_url + or ( + env["ir.config_parameter"].sudo().get_param("web.base.url") + + endpoint.root_path + ) + ) + response = RedirectResponse(url=base) + helper._set_auth_cookie(auth_partner, request, response) + return response + + +class AuthService(models.AbstractModel): + _name = "fastapi.auth.service" + _description = "Fastapi Auth Service" + + endpoint_id = fields.Many2one("fastapi.endpoint", required=True) + directory_id = fields.Many2one("auth.directory") + + def new(self, vals, **kwargs): + rec = super().new(vals, **kwargs) + # Can't have computed / related field in AbstractModel + rec.directory_id = rec.endpoint_id.directory_id + # Auto add endpoint context for mail context + return rec.with_context(_fastapi_endpoint_id=vals["endpoint_id"].id) + + def _get_auth_from_partner(self, partner): + return partner._get_auth_partner_for_directory(self.directory_id) + + def _signup(self, data): + auth_partner = ( + self.env["auth.partner"].sudo()._signup(self.directory_id, **data.dict()) + ) + return auth_partner + + def _login(self, data): + return self.env["auth.partner"].sudo()._login(self.directory_id, **data.dict()) + + def _impersonate(self, token): + return self.env["auth.partner"].sudo()._impersonating(self.directory_id, token) + + def _logout(self): + pass + + def _set_password(self, data): + return ( + self.env["auth.partner"] + .sudo() + ._set_password(self.directory_id, data.token, data.password) + ) + + def _request_reset_password(self, data): + # There can be only one auth_partner per login per directory + auth_partner = ( + self.env["auth.partner"] + .sudo() + .search( + [ + ("directory_id", "=", self.directory_id.id), + ("login", "=", data.login.lower()), + ] + ) + ) + + if not auth_partner: + # do not leak information, no partner no mail sent + return + + return auth_partner.sudo()._request_reset_password() + + def _validate_email(self, data): + return ( + self.env["auth.partner"] + .sudo() + ._validate_email(self.directory_id, data.token) + ) + + def _prepare_cookie_payload(self, partner): + # use short key to reduce cookie size + return { + "did": self.directory_id.id, + "pid": partner.id, + } + + def _prepare_cookie(self, partner): + secret = self.directory_id.cookie_secret_key or self.directory_id.secret_key + if not secret: + raise ValidationError(_("No cookie secret key defined")) + payload = self._prepare_cookie_payload(partner) + value = URLSafeTimedSerializer(secret).dumps(payload) + exp = ( + datetime.now(timezone.utc) + + timedelta(minutes=self.directory_id.cookie_duration) + ).timestamp() + vals = { + "value": value, + "expires": exp, + "httponly": True, + "secure": True, + "samesite": "strict", + } + if tools.config.get("test_enable"): + # do not force https for test + vals["secure"] = False + return vals + + def _set_auth_cookie(self, auth_partner, request, response): + response.set_cookie( + COOKIE_AUTH_NAME, **self.sudo()._prepare_cookie(auth_partner.partner_id) + ) + + def _clear_auth_cookie(self, response): + response.set_cookie(COOKIE_AUTH_NAME, max_age=0) diff --git a/fastapi_auth_partner/schemas.py b/fastapi_auth_partner/schemas.py new file mode 100644 index 000000000..27bec5f05 --- /dev/null +++ b/fastapi_auth_partner/schemas.py @@ -0,0 +1,40 @@ +# Copyright 2024 Akretion (https://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from extendable_pydantic import StrictExtendableBaseModel + + +class AuthLoginInput(StrictExtendableBaseModel): + login: str + password: str + + +class AuthRegisterInput(StrictExtendableBaseModel): + name: str + login: str + password: str + + +class AuthForgetPasswordInput(StrictExtendableBaseModel): + login: str + + +class AuthSetPasswordInput(StrictExtendableBaseModel): + token: str + password: str + + +class AuthValidateEmailInput(StrictExtendableBaseModel): + token: str + + +class AuthPartnerResponse(StrictExtendableBaseModel): + login: str + mail_verified: bool + + @classmethod + def from_auth_partner(cls, odoo_rec): + return cls.model_construct( + login=odoo_rec.login, mail_verified=odoo_rec.mail_verified + ) diff --git a/fastapi_auth_partner/security/ir.model.access.csv b/fastapi_auth_partner/security/ir.model.access.csv new file mode 100644 index 000000000..b52ae0751 --- /dev/null +++ b/fastapi_auth_partner/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +api_access_fastapi_wizard_auth_partner_impersonate,fastapi_wizard_auth_partner_impersonate,model_wizard_auth_partner_impersonate,auth_partner.group_auth_partner_manager,1,1,1,1 diff --git a/fastapi_auth_partner/security/res_group.xml b/fastapi_auth_partner/security/res_group.xml new file mode 100644 index 000000000..c7f87fb9e --- /dev/null +++ b/fastapi_auth_partner/security/res_group.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/fastapi_auth_partner/static/description/index.html b/fastapi_auth_partner/static/description/index.html new file mode 100644 index 000000000..7eb80ab0a --- /dev/null +++ b/fastapi_auth_partner/static/description/index.html @@ -0,0 +1,477 @@ + + + + + +Fastapi Auth Partner + + + +
+

Fastapi Auth Partner

+ + +

Beta License: AGPL-3 OCA/rest-framework Translate me on Weblate Try me on Runboat

+

This module is the FastAPI implementation of auth_partner +it provides all the routes to manage the authentication of partners.

+

Table of contents

+ +
+

Usage

+

First you have to add the auth router to your FastAPI endpoint and the authentication dependency to your app dependencies:

+
+from odoo.addons.fastapi import dependencies
+from odoo.addons.fastapi_auth_partner.dependencies import (
+  auth_partner_authenticated_partner,
+)
+from odoo.addons.fastapi_auth_partner.routers.auth import auth_router
+
+class FastapiEndpoint(models.Model):
+    _inherit = "fastapi.endpoint"
+
+    def _get_fastapi_routers(self):
+      if self.app == "myapp":
+          return [
+              auth_router,
+          ]
+      return super()._get_fastapi_routers()
+
+    def _get_app_dependencies_overrides(self):
+        res = super()._get_app_dependencies_overrides()
+        if self.app == "myapp":
+            res.update(
+                {
+                    dependencies.authenticated_partner_impl: auth_partner_authenticated_partner,
+                }
+            )
+        return res
+
+

Next you can manage your authenticable partners and directories in the Odoo interface:

+

FastAPI > Authentication > Partner

+

and

+

FastAPI > Authentication > Directory

+

Next you must set the directory used for the authentication in the FastAPI endpoint:

+

FastAPI > FastAPI Endpoint > myapp > Directory

+

Then you can use the auth router to authenticate your requests:

+
    +
  • POST /auth/register to register a partner
  • +
  • POST /auth/login to authenticate a partner
  • +
  • POST /auth/logout to unauthenticate a partner
  • +
  • POST /auth/validate_email to validate a partner email
  • +
  • POST /auth/request_reset_password to request a password reset
  • +
  • POST /auth/set_password to set a new password
  • +
  • GET /auth/profile to get the partner profile
  • +
  • GET /auth/impersonate to impersonate a partner
  • +
+
+
+

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

+
    +
  • Akretion
  • +
+
+
+

Contributors

+
    +
  • Akretion:
      +
    • Sébastien Beau
    • +
    • Florian Mounier
    • +
    +
  • +
+
+
+

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.

+

This module is part of the OCA/rest-framework project on GitHub.

+

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

+
+
+
+ + diff --git a/fastapi_auth_partner/tests/__init__.py b/fastapi_auth_partner/tests/__init__.py new file mode 100644 index 000000000..021c23763 --- /dev/null +++ b/fastapi_auth_partner/tests/__init__.py @@ -0,0 +1,2 @@ +from . import test_auth +from . import test_fastapi_auth_partner_demo diff --git a/fastapi_auth_partner/tests/test_auth.py b/fastapi_auth_partner/tests/test_auth.py new file mode 100644 index 000000000..04f24ccf5 --- /dev/null +++ b/fastapi_auth_partner/tests/test_auth.py @@ -0,0 +1,243 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import json +from contextlib import contextmanager +from functools import partial + +from requests import Response + +from odoo.tests.common import tagged +from odoo.tools import mute_logger + +from odoo.addons.auth_partner.tests.common import CommonTestAuthPartner +from odoo.addons.extendable_fastapi.tests.common import FastAPITransactionCase +from odoo.addons.fastapi.dependencies import fastapi_endpoint + +from fastapi import status + +from ..routers.auth import auth_router + + +class CommonTestAuth(FastAPITransactionCase): + @contextmanager + def _create_test_client(self, **kwargs): + self.env.invalidate_all() + with mute_logger("httpx"): + with super()._create_test_client(**kwargs) as test_client: + yield test_client + + @classmethod + def setUpClass(cls) -> None: + super().setUpClass() + cls.demo_app = cls.env.ref("fastapi_auth_partner.fastapi_endpoint_demo") + cls.env = cls.env(context=dict(cls.env.context, queue_job__no_delay=True)) + cls.default_fastapi_router = auth_router + cls.default_fastapi_app = cls.demo_app._get_app() + cls.default_fastapi_dependency_overrides = { + fastapi_endpoint: partial(lambda a: a, cls.demo_app) + } + cls.default_fastapi_odoo_env = cls.env + cls.default_fastapi_running_user = cls.demo_app.user_id + + def _register_partner(self): + with self._create_test_client() as test_client, self.new_mails() as new_mails: + response: Response = test_client.post( + "/auth/register", + content=json.dumps( + { + "name": "Loriot", + "login": "loriot@example.org", + "password": "supersecret", + } + ), + ) + self.assertEqual(response.status_code, status.HTTP_201_CREATED, response.text) + return response, new_mails + + def _login(self, test_client, password="supersecret"): + response: Response = test_client.post( + "/auth/login", + content=json.dumps( + { + "login": "loriot@example.org", + "password": password, + } + ), + ) + self.assertEqual(response.status_code, status.HTTP_200_OK, response.text) + return response + + +@tagged("post_install", "-at_install") +class TestFastapiAuthPartner(CommonTestAuth, CommonTestAuthPartner): + def test_register(self): + response, new_mails = self._register_partner() + self.assertEqual(response.status_code, status.HTTP_201_CREATED, response.text) + self.assertEqual( + response.json(), {"login": "loriot@example.org", "mail_verified": False} + ) + self.assertEqual(len(new_mails), 1) + self.assertIn( + "please click on the following link to verify your email", + str(new_mails.body), + ) + + def test_login(self): + self._register_partner() + with self._create_test_client() as test_client: + response = self._login(test_client) + self.assertEqual( + response.json(), {"login": "loriot@example.org", "mail_verified": False} + ) + + def test_logout(self): + self._register_partner() + with self._create_test_client() as test_client: + response: Response = test_client.post("/auth/logout") + self.assertEqual( + response.status_code, status.HTTP_205_RESET_CONTENT, response.text + ) + + def test_request_reset_password(self): + self._register_partner() + with self._create_test_client() as test_client, self.new_mails() as new_mails: + response: Response = test_client.post( + "/auth/request_reset_password", + content=json.dumps({"login": "loriot@example.org"}), + ) + self.assertEqual(response.status_code, status.HTTP_200_OK, response.text) + self.assertFalse( + self.env["auth.partner"] + .search([("login", "=", "loriot@example.org")]) + .mail_verified, + ) + self.assertEqual(len(new_mails), 1) + self.assertIn( + "Click on the following link to reset your password", + str(new_mails.body), + ) + token = str(new_mails.body).split("token=")[1].split('">')[0] + response: Response = test_client.post( + "/auth/set_password", + content=json.dumps( + { + "password": "megasecret", + "token": token, + } + ), + ) + self.assertEqual(response.status_code, status.HTTP_200_OK, response.text) + + self.assertTrue( + self.env["auth.partner"] + .search([("login", "=", "loriot@example.org")]) + .mail_verified, + ) + response = self._login(test_client, password="megasecret") + self.assertEqual( + response.json(), {"login": "loriot@example.org", "mail_verified": True} + ) + + def test_validate_email(self): + self._register_partner() + mail = self.env["mail.mail"].search([], limit=1, order="id desc") + self.assertIn( + "please click on the following link to verify your email", str(mail.body) + ) + self.assertFalse( + self.env["auth.partner"] + .search([("login", "=", "loriot@example.org")]) + .mail_verified, + ) + token = str(mail.body).split("token=")[1].split('">')[0] + with self._create_test_client() as test_client: + response: Response = test_client.post( + "/auth/validate_email", + content=json.dumps({"token": token}), + ) + self.assertEqual(response.status_code, status.HTTP_200_OK, response.text) + + self.assertTrue( + self.env["auth.partner"] + .search([("login", "=", "loriot@example.org")]) + .mail_verified, + ) + + def test_impersonate(self): + self.demo_app.public_url = self.demo_app.public_api_url = False + self._register_partner() + auth_partner = self.env["auth.partner"].search( + [("login", "=", "loriot@example.org")] + ) + self.assertEqual(len(auth_partner), 1) + action = auth_partner.with_user(self.env.ref("base.user_admin")).impersonate() + url = action["url"].split("fastapi_auth_partner_demo", 1)[1] + + with self._create_test_client() as test_client: + response: Response = test_client.get(url, follow_redirects=False) + self.assertEqual(response.status_code, status.HTTP_307_TEMPORARY_REDIRECT) + self.assertTrue( + response.headers["location"].endswith("/fastapi_auth_partner_demo") + ) + self.assertIn("fastapi_auth_partner", response.cookies) + + def test_impersonate_api_url(self): + self._register_partner() + auth_partner = self.env["auth.partner"].search( + [("login", "=", "loriot@example.org")] + ) + self.assertEqual(len(auth_partner), 1) + action = auth_partner.with_user(self.env.ref("base.user_admin")).impersonate() + self.assertTrue( + action["url"].startswith("https://api.example.com/auth/impersonate/") + ) + action["url"].split("auth/impersonate/", 1)[1] + + def test_wizard_auth_partner_impersonate(self): + self._register_partner() + action = ( + self.env["wizard.auth.partner.impersonate"] + .create( + { + "auth_partner_id": self.env["auth.partner"] + .search([("login", "=", "loriot@example.org")]) + .id, + "fastapi_endpoint_id": self.demo_app.id, + } + ) + .with_user(self.env.ref("base.user_admin")) + .action_impersonate() + ) + self.assertTrue( + action["url"].startswith("https://api.example.com/auth/impersonate/") + ) + + def test_wizard_auth_partner_reset_password(self): + self._register_partner() + + template = self.env.ref("auth_partner.email_reset_password") + template.body_html = template.body_html.replace( + "https://example.org/", "{{ object.env.context['public_url'] }}" + ) + with self.new_mails() as new_mails: + self.env["wizard.auth.partner.reset.password"].create( + { + "delay": "2-days", + "template_id": template.id, + "fastapi_endpoint_id": self.demo_app.id, + } + ).with_context( + active_ids=self.env["auth.partner"] + .search([("login", "=", "loriot@example.org")]) + .ids + ).action_reset_password() + + self.assertEqual(len(new_mails), 1) + self.assertIn( + "Click on the following link to reset your password", str(new_mails.body) + ) + self.assertIn( + "https://www.example.com/password/reset?token=", str(new_mails.body) + ) diff --git a/fastapi_auth_partner/tests/test_fastapi_auth_partner_demo.py b/fastapi_auth_partner/tests/test_fastapi_auth_partner_demo.py new file mode 100644 index 000000000..0539cef17 --- /dev/null +++ b/fastapi_auth_partner/tests/test_fastapi_auth_partner_demo.py @@ -0,0 +1,93 @@ +# Copyright 2023 ACSONE SA/NV +# Copyright 2023 Akretion (https://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import json +import sys + +from odoo import tests + +from odoo.addons.base.models.res_partner import Partner +from odoo.addons.fastapi_auth_partner.dependencies import AuthPartner + +from fastapi import Depends, status + +if sys.version_info >= (3, 9): + from typing import Annotated +else: + from typing_extensions import Annotated + +from odoo.addons.fastapi_auth_partner.routers.auth import auth_router +from odoo.addons.fastapi_auth_partner.schemas import AuthPartnerResponse + + +@auth_router.get("/auth/whoami-public-or-partner") +def whoami_public_or_partner( + partner: Annotated[ + Partner, + Depends(AuthPartner(allow_unauthenticated=True)), + ], +) -> AuthPartnerResponse: + if partner: + return AuthPartnerResponse.from_auth_partner(partner.auth_partner_ids) + return AuthPartnerResponse(login="no-one", mail_verified=False) + + +@tests.tagged("post_install", "-at_install") +class TestEndToEnd(tests.HttpCase): + def setUp(self): + super().setUp() + endpoint = self.env.ref("fastapi_auth_partner.fastapi_endpoint_demo") + endpoint._handle_registry_sync() + + self.fastapi_demo_app = self.env.ref("fastapi.fastapi_endpoint_demo") + self.fastapi_demo_app._handle_registry_sync() + + def _register_partner(self): + return self.url_open( + "/fastapi_auth_partner_demo/auth/register", + timeout=1000, + data=json.dumps( + { + "name": "Loriot", + "login": "loriot@example.org", + "password": "supersecret", + } + ), + ) + + def test_register(self): + response = self._register_partner() + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual( + response.json(), {"login": "loriot@example.org", "mail_verified": False} + ) + self.assertIn("fastapi_auth_partner", response.cookies) + + def test_profile(self): + self._register_partner() + resp = self.url_open("/fastapi_auth_partner_demo/auth/profile") + resp.raise_for_status() + data = resp.json() + self.assertEqual( + data, + {"login": "loriot@example.org", "mail_verified": False}, + ) + + def test_profile_forbidden(self): + """A end-to-end test with negative authentication.""" + resp = self.url_open("/fastapi_auth_partner_demo/auth/profile") + self.assertEqual(resp.status_code, 401) + + def test_public(self): + """A end-to-end test for anonymous/public access.""" + resp = self.url_open("/fastapi_auth_partner_demo/auth/whoami-public-or-partner") + self.assertEqual(resp.status_code, 200) + self.assertEqual(resp.json(), {"login": "no-one", "mail_verified": False}) + + self._register_partner() + resp = self.url_open("/fastapi_auth_partner_demo/auth/whoami-public-or-partner") + self.assertEqual( + resp.json(), {"login": "loriot@example.org", "mail_verified": False} + ) diff --git a/fastapi_auth_partner/views/auth_directory_view.xml b/fastapi_auth_partner/views/auth_directory_view.xml new file mode 100644 index 000000000..fd115531c --- /dev/null +++ b/fastapi_auth_partner/views/auth_directory_view.xml @@ -0,0 +1,29 @@ + + + + auth.directory + + +
+
+ + + + + + +
+ + +
diff --git a/fastapi_auth_partner/views/auth_partner_view.xml b/fastapi_auth_partner/views/auth_partner_view.xml new file mode 100644 index 000000000..0417e1f32 --- /dev/null +++ b/fastapi_auth_partner/views/auth_partner_view.xml @@ -0,0 +1,31 @@ + + + + auth.partner + + + + + + + + + diff --git a/fastapi_auth_partner/views/fastapi_endpoint_view.xml b/fastapi_auth_partner/views/fastapi_endpoint_view.xml new file mode 100644 index 000000000..10c72e35d --- /dev/null +++ b/fastapi_auth_partner/views/fastapi_endpoint_view.xml @@ -0,0 +1,25 @@ + + + + fastapi.endpoint + + + + + + + + + + + + + + + diff --git a/fastapi_auth_partner/wizards/__init__.py b/fastapi_auth_partner/wizards/__init__.py new file mode 100644 index 000000000..adc3f5233 --- /dev/null +++ b/fastapi_auth_partner/wizards/__init__.py @@ -0,0 +1,2 @@ +from . import wizard_auth_partner_impersonate +from . import wizard_auth_partner_reset_password diff --git a/fastapi_auth_partner/wizards/wizard_auth_partner_impersonate.py b/fastapi_auth_partner/wizards/wizard_auth_partner_impersonate.py new file mode 100644 index 000000000..8d04cef3c --- /dev/null +++ b/fastapi_auth_partner/wizards/wizard_auth_partner_impersonate.py @@ -0,0 +1,29 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import fields, models + + +class WizardAuthPartnerImpersonate(models.TransientModel): + _name = "wizard.auth.partner.impersonate" + _description = "Wizard Partner Auth Impersonate" + + auth_partner_id = fields.Many2one( + "auth.partner", + required=True, + ) + auth_directory_id = fields.Many2one( + "auth.directory", + related="auth_partner_id.directory_id", + ) + fastapi_endpoint_id = fields.Many2one( + "fastapi.endpoint", + required=True, + ) + + def action_impersonate(self): + return self.auth_partner_id.with_context( + fastapi_endpoint_id=self.fastapi_endpoint_id.id + ).impersonate() diff --git a/fastapi_auth_partner/wizards/wizard_auth_partner_impersonate_view.xml b/fastapi_auth_partner/wizards/wizard_auth_partner_impersonate_view.xml new file mode 100644 index 000000000..e9640c5df --- /dev/null +++ b/fastapi_auth_partner/wizards/wizard_auth_partner_impersonate_view.xml @@ -0,0 +1,43 @@ + + + + + wizard.auth.partner.impersonate + +
+ Please choose an endpoint: + + + + + +
+
+ +
+
+
+ + + Impersonate + wizard.auth.partner.impersonate + ir.actions.act_window + form + new + + + +
diff --git a/fastapi_auth_partner/wizards/wizard_auth_partner_reset_password.py b/fastapi_auth_partner/wizards/wizard_auth_partner_reset_password.py new file mode 100644 index 000000000..eed208113 --- /dev/null +++ b/fastapi_auth_partner/wizards/wizard_auth_partner_reset_password.py @@ -0,0 +1,18 @@ +# Copyright 2024 Akretion (https://www.akretion.com). +# @author Florian Mounier +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class WizardAuthPartnerResetPassword(models.TransientModel): + _inherit = "wizard.auth.partner.reset.password" + + fastapi_endpoint_id = fields.Many2one( + "fastapi.endpoint", + ) + + def action_reset_password(self): + if self.fastapi_endpoint_id: + self = self.with_context(_fastapi_endpoint_id=self.fastapi_endpoint_id.id) + return super(WizardAuthPartnerResetPassword, self).action_reset_password() diff --git a/fastapi_auth_partner/wizards/wizard_auth_partner_reset_password_view.xml b/fastapi_auth_partner/wizards/wizard_auth_partner_reset_password_view.xml new file mode 100644 index 000000000..1495e72d0 --- /dev/null +++ b/fastapi_auth_partner/wizards/wizard_auth_partner_reset_password_view.xml @@ -0,0 +1,17 @@ + + + + + wizard.auth.partner.reset.password + + + + + + + + + From 55b6f90af5f44f1816fc2688de1776feb928f735 Mon Sep 17 00:00:00 2001 From: oca-ci Date: Tue, 3 Jun 2025 14:39:35 +0000 Subject: [PATCH 2/9] [UPD] Update fastapi_auth_partner.pot --- .../i18n/fastapi_auth_partner.pot | 263 ++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 fastapi_auth_partner/i18n/fastapi_auth_partner.pot diff --git a/fastapi_auth_partner/i18n/fastapi_auth_partner.pot b/fastapi_auth_partner/i18n/fastapi_auth_partner.pot new file mode 100644 index 000000000..fd3569e94 --- /dev/null +++ b/fastapi_auth_partner/i18n/fastapi_auth_partner.pot @@ -0,0 +1,263 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fastapi_auth_partner +# +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: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__app +msgid "App" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model,name:fastapi_auth_partner.model_auth_directory +msgid "Auth Directory" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model,name:fastapi_auth_partner.model_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__auth_partner_id +msgid "Auth Partner" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.ui.menu,name:fastapi_auth_partner.fastapi_auth +msgid "Authentication" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__demo_auth_method +msgid "Authentication method" +msgstr "" + +#. module: fastapi_auth_partner +#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form +msgid "Cancel" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__cookie_duration +msgid "Cookie Duration" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__cookie_secret_key +msgid "Cookie Secret Key" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__create_uid +msgid "Created by" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__create_date +msgid "Created on" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields.selection,name:fastapi_auth_partner.selection__fastapi_endpoint__app__demo +msgid "Demo Endpoint" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_auth_service__directory_id +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__directory_id +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__auth_directory_id +#: model:ir.ui.menu,name:fastapi_auth_partner.auth_directory_menu +msgid "Directory" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__display_name +msgid "Display Name" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_auth_service__endpoint_id +msgid "Endpoint" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model,name:fastapi_auth_partner.model_fastapi_endpoint +msgid "FastAPI Endpoint" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__fastapi_endpoint_ids +msgid "FastAPI Endpoints" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model,name:fastapi_auth_partner.model_fastapi_auth_service +msgid "Fastapi Auth Service" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__fastapi_endpoint_id +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_reset_password__fastapi_endpoint_id +msgid "Fastapi Endpoint" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__id +msgid "ID" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.actions.act_window,name:fastapi_auth_partner.auth_partner_action_impersonate +#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form +msgid "Impersonate" +msgstr "" + +#. module: fastapi_auth_partner +#. odoo-python +#: code:addons/fastapi_auth_partner/models/auth_partner.py:0 +#, python-format +msgid "Impersonation successful" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,help:fastapi_auth_partner.field_auth_directory__cookie_duration +msgid "In minute, default 525600 minutes => 1 year" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__is_auth_partner +msgid "Is Auth Partner" +msgstr "" + +#. module: fastapi_auth_partner +#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form +msgid "Label" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate____last_update +msgid "Last Modified on" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__write_date +msgid "Last Updated on" +msgstr "" + +#. module: fastapi_auth_partner +#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.auth_partner_view_form +msgid "Local Impersonate" +msgstr "" + +#. module: fastapi_auth_partner +#. odoo-python +#: code:addons/fastapi_auth_partner/routers/auth.py:0 +#, python-format +msgid "No cookie secret key defined" +msgstr "" + +#. module: fastapi_auth_partner +#. odoo-python +#: code:addons/fastapi_auth_partner/models/auth_partner.py:0 +#, python-format +msgid "Only admin can impersonate locally" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.ui.menu,name:fastapi_auth_partner.auth_partner_menu +msgid "Partner" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields.selection,name:fastapi_auth_partner.selection__fastapi_endpoint__demo_auth_method__auth_partner +msgid "Partner Auth" +msgstr "" + +#. module: fastapi_auth_partner +#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form +msgid "Please choose an endpoint:" +msgstr "" + +#. module: fastapi_auth_partner +#. odoo-python +#: code:addons/fastapi_auth_partner/models/auth_partner.py:0 +#, python-format +msgid "Please install base_future_response for local impersonate to work" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__public_api_url +msgid "Public Api Url" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__public_url +msgid "Public Url" +msgstr "" + +#. module: fastapi_auth_partner +#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.auth_directory_view_form +msgid "Regenerate cookie secret key" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__sliding_session +msgid "Sliding Session" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,help:fastapi_auth_partner.field_fastapi_endpoint__is_auth_partner +msgid "Technical field to know if the auth method is partner" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,help:fastapi_auth_partner.field_fastapi_endpoint__public_api_url +msgid "" +"The public URL of the API.\n" +"This URL is used in impersonation to set the cookie on the right API domain if you use a reverse proxy to serve the API.\n" +"Defaults to the public_url if not set or the odoo url if not set either." +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,help:fastapi_auth_partner.field_fastapi_endpoint__public_url +msgid "" +"The public URL of the site.\n" +"This URL is used for the impersonation final redirect. And can also be used in the mail template to construct links.\n" +"Default to the public_api_url if not set or the odoo url if not set either." +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,help:fastapi_auth_partner.field_auth_directory__cookie_secret_key +msgid "The secret key used to sign the cookie" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model,name:fastapi_auth_partner.model_wizard_auth_partner_impersonate +msgid "Wizard Partner Auth Impersonate" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model,name:fastapi_auth_partner.model_wizard_auth_partner_reset_password +msgid "Wizard Partner Auth Reset Password" +msgstr "" + +#. module: fastapi_auth_partner +#. odoo-python +#: code:addons/fastapi_auth_partner/models/auth_partner.py:0 +#, python-format +msgid "" +"You are now impersonating %s\n" +"%%s" +msgstr "" From d4531a72ac002523fde48acf050e2b2fb86a2e99 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 3 Jun 2025 14:42:54 +0000 Subject: [PATCH 3/9] [BOT] post-merge updates --- fastapi_auth_partner/README.rst | 8 +++-- .../static/description/icon.png | Bin 0 -> 10254 bytes .../static/description/index.html | 28 +++++++++++------- 3 files changed, 23 insertions(+), 13 deletions(-) create mode 100644 fastapi_auth_partner/static/description/icon.png diff --git a/fastapi_auth_partner/README.rst b/fastapi_auth_partner/README.rst index 69e036674..fc8471c13 100644 --- a/fastapi_auth_partner/README.rst +++ b/fastapi_auth_partner/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ==================== Fastapi Auth Partner ==================== @@ -7,13 +11,13 @@ Fastapi Auth Partner !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:57e60d4a203cd5e613e07fe0c1f6a207bd6b77f537ed5cbbbe72a1cc2184f0de + !! source digest: sha256:2ebd9377ca7b035ab9fb0383513aacb5ca8645f69d5d85c171883b40b439017e !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Frest--framework-lightgray.png?logo=github diff --git a/fastapi_auth_partner/static/description/icon.png b/fastapi_auth_partner/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1dcc49c24f364e9adf0afbc6fc0bac6dbecdeb11 GIT binary patch literal 10254 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 -Fastapi Auth Partner +README.rst -
-

Fastapi Auth Partner

+
+ + +Odoo Community Association + +
+

Fastapi Auth Partner

-

Beta License: AGPL-3 OCA/rest-framework Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/rest-framework Translate me on Weblate Try me on Runboat

This module is the FastAPI implementation of auth_partner it provides all the routes to manage the authentication of partners.

Table of contents

@@ -386,7 +391,7 @@

Fastapi Auth Partner

-

Usage

+

Usage

First you have to add the auth router to your FastAPI endpoint and the authentication dependency to your app dependencies:

 from odoo.addons.fastapi import dependencies
@@ -434,7 +439,7 @@ 

Usage

-

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 @@ -442,15 +447,15 @@

Bug Tracker

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

-

Credits

+

Credits

-

Authors

+

Authors

  • Akretion
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -473,5 +478,6 @@

Maintainers

+
From 25ca683538d249d1c20cc33251f5627885beccd2 Mon Sep 17 00:00:00 2001 From: mymage Date: Wed, 4 Jun 2025 09:40:03 +0000 Subject: [PATCH 4/9] Added translation using Weblate (Italian) --- fastapi_auth_partner/i18n/it.po | 264 ++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 fastapi_auth_partner/i18n/it.po diff --git a/fastapi_auth_partner/i18n/it.po b/fastapi_auth_partner/i18n/it.po new file mode 100644 index 000000000..2e8ddc374 --- /dev/null +++ b/fastapi_auth_partner/i18n/it.po @@ -0,0 +1,264 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fastapi_auth_partner +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\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" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__app +msgid "App" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model,name:fastapi_auth_partner.model_auth_directory +msgid "Auth Directory" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model,name:fastapi_auth_partner.model_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__auth_partner_id +msgid "Auth Partner" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.ui.menu,name:fastapi_auth_partner.fastapi_auth +msgid "Authentication" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__demo_auth_method +msgid "Authentication method" +msgstr "" + +#. module: fastapi_auth_partner +#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form +msgid "Cancel" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__cookie_duration +msgid "Cookie Duration" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__cookie_secret_key +msgid "Cookie Secret Key" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__create_uid +msgid "Created by" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__create_date +msgid "Created on" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields.selection,name:fastapi_auth_partner.selection__fastapi_endpoint__app__demo +msgid "Demo Endpoint" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_auth_service__directory_id +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__directory_id +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__auth_directory_id +#: model:ir.ui.menu,name:fastapi_auth_partner.auth_directory_menu +msgid "Directory" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__display_name +msgid "Display Name" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_auth_service__endpoint_id +msgid "Endpoint" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model,name:fastapi_auth_partner.model_fastapi_endpoint +msgid "FastAPI Endpoint" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__fastapi_endpoint_ids +msgid "FastAPI Endpoints" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model,name:fastapi_auth_partner.model_fastapi_auth_service +msgid "Fastapi Auth Service" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__fastapi_endpoint_id +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_reset_password__fastapi_endpoint_id +msgid "Fastapi Endpoint" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__id +msgid "ID" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.actions.act_window,name:fastapi_auth_partner.auth_partner_action_impersonate +#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form +msgid "Impersonate" +msgstr "" + +#. module: fastapi_auth_partner +#. odoo-python +#: code:addons/fastapi_auth_partner/models/auth_partner.py:0 +#, python-format +msgid "Impersonation successful" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,help:fastapi_auth_partner.field_auth_directory__cookie_duration +msgid "In minute, default 525600 minutes => 1 year" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__is_auth_partner +msgid "Is Auth Partner" +msgstr "" + +#. module: fastapi_auth_partner +#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form +msgid "Label" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate____last_update +msgid "Last Modified on" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__write_date +msgid "Last Updated on" +msgstr "" + +#. module: fastapi_auth_partner +#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.auth_partner_view_form +msgid "Local Impersonate" +msgstr "" + +#. module: fastapi_auth_partner +#. odoo-python +#: code:addons/fastapi_auth_partner/routers/auth.py:0 +#, python-format +msgid "No cookie secret key defined" +msgstr "" + +#. module: fastapi_auth_partner +#. odoo-python +#: code:addons/fastapi_auth_partner/models/auth_partner.py:0 +#, python-format +msgid "Only admin can impersonate locally" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.ui.menu,name:fastapi_auth_partner.auth_partner_menu +msgid "Partner" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields.selection,name:fastapi_auth_partner.selection__fastapi_endpoint__demo_auth_method__auth_partner +msgid "Partner Auth" +msgstr "" + +#. module: fastapi_auth_partner +#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form +msgid "Please choose an endpoint:" +msgstr "" + +#. module: fastapi_auth_partner +#. odoo-python +#: code:addons/fastapi_auth_partner/models/auth_partner.py:0 +#, python-format +msgid "Please install base_future_response for local impersonate to work" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__public_api_url +msgid "Public Api Url" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__public_url +msgid "Public Url" +msgstr "" + +#. module: fastapi_auth_partner +#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.auth_directory_view_form +msgid "Regenerate cookie secret key" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__sliding_session +msgid "Sliding Session" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,help:fastapi_auth_partner.field_fastapi_endpoint__is_auth_partner +msgid "Technical field to know if the auth method is partner" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,help:fastapi_auth_partner.field_fastapi_endpoint__public_api_url +msgid "" +"The public URL of the API.\n" +"This URL is used in impersonation to set the cookie on the right API domain if you use a reverse proxy to serve the API.\n" +"Defaults to the public_url if not set or the odoo url if not set either." +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,help:fastapi_auth_partner.field_fastapi_endpoint__public_url +msgid "" +"The public URL of the site.\n" +"This URL is used for the impersonation final redirect. And can also be used in the mail template to construct links.\n" +"Default to the public_api_url if not set or the odoo url if not set either." +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model.fields,help:fastapi_auth_partner.field_auth_directory__cookie_secret_key +msgid "The secret key used to sign the cookie" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model,name:fastapi_auth_partner.model_wizard_auth_partner_impersonate +msgid "Wizard Partner Auth Impersonate" +msgstr "" + +#. module: fastapi_auth_partner +#: model:ir.model,name:fastapi_auth_partner.model_wizard_auth_partner_reset_password +msgid "Wizard Partner Auth Reset Password" +msgstr "" + +#. module: fastapi_auth_partner +#. odoo-python +#: code:addons/fastapi_auth_partner/models/auth_partner.py:0 +#, python-format +msgid "" +"You are now impersonating %s\n" +"%%s" +msgstr "" From fb96e60280cbd457c1b8b931dca33e48a766cd9e Mon Sep 17 00:00:00 2001 From: mymage Date: Wed, 4 Jun 2025 14:12:24 +0000 Subject: [PATCH 5/9] Translated using Weblate (Italian) Currently translated at 100.0% (45 of 45 strings) Translation: rest-framework-16.0/rest-framework-16.0-fastapi_auth_partner Translate-URL: https://translation.odoo-community.org/projects/rest-framework-16-0/rest-framework-16-0-fastapi_auth_partner/it/ --- fastapi_auth_partner/i18n/it.po | 100 ++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/fastapi_auth_partner/i18n/it.po b/fastapi_auth_partner/i18n/it.po index 2e8ddc374..6add7ce35 100644 --- a/fastapi_auth_partner/i18n/it.po +++ b/fastapi_auth_partner/i18n/it.po @@ -6,69 +6,71 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 16.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: Automatically generated\n" +"PO-Revision-Date: 2025-06-04 16:26+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 5.10.4\n" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__app msgid "App" -msgstr "" +msgstr "Applicazione" #. module: fastapi_auth_partner #: model:ir.model,name:fastapi_auth_partner.model_auth_directory msgid "Auth Directory" -msgstr "" +msgstr "Cartella autorizzazione" #. module: fastapi_auth_partner #: model:ir.model,name:fastapi_auth_partner.model_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__auth_partner_id msgid "Auth Partner" -msgstr "" +msgstr "Partner autorizzazione" #. module: fastapi_auth_partner #: model:ir.ui.menu,name:fastapi_auth_partner.fastapi_auth msgid "Authentication" -msgstr "" +msgstr "Autenticazione" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__demo_auth_method msgid "Authentication method" -msgstr "" +msgstr "Metodo autenticazione" #. module: fastapi_auth_partner #: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form msgid "Cancel" -msgstr "" +msgstr "Annulla" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__cookie_duration msgid "Cookie Duration" -msgstr "" +msgstr "Durata cookie" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__cookie_secret_key msgid "Cookie Secret Key" -msgstr "" +msgstr "Chiave segreta cookie" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__create_uid msgid "Created by" -msgstr "" +msgstr "Creato da" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__create_date msgid "Created on" -msgstr "" +msgstr "Creato il" #. module: fastapi_auth_partner #: model:ir.model.fields.selection,name:fastapi_auth_partner.selection__fastapi_endpoint__app__demo msgid "Demo Endpoint" -msgstr "" +msgstr "Endpoint esempio" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_auth_service__directory_id @@ -76,152 +78,152 @@ msgstr "" #: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__auth_directory_id #: model:ir.ui.menu,name:fastapi_auth_partner.auth_directory_menu msgid "Directory" -msgstr "" +msgstr "Cartella" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__display_name msgid "Display Name" -msgstr "" +msgstr "Nome visualizzato" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_auth_service__endpoint_id msgid "Endpoint" -msgstr "" +msgstr "Endpoint" #. module: fastapi_auth_partner #: model:ir.model,name:fastapi_auth_partner.model_fastapi_endpoint msgid "FastAPI Endpoint" -msgstr "" +msgstr "Endopoint FastAPI" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__fastapi_endpoint_ids msgid "FastAPI Endpoints" -msgstr "" +msgstr "Endpoint FastAPI" #. module: fastapi_auth_partner #: model:ir.model,name:fastapi_auth_partner.model_fastapi_auth_service msgid "Fastapi Auth Service" -msgstr "" +msgstr "Servizio autenticazione FastAPI" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__fastapi_endpoint_id #: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_reset_password__fastapi_endpoint_id msgid "Fastapi Endpoint" -msgstr "" +msgstr "Endopoint FastAPI" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__id msgid "ID" -msgstr "" +msgstr "ID" #. module: fastapi_auth_partner #: model:ir.actions.act_window,name:fastapi_auth_partner.auth_partner_action_impersonate #: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form msgid "Impersonate" -msgstr "" +msgstr "Imita" #. module: fastapi_auth_partner #. odoo-python #: code:addons/fastapi_auth_partner/models/auth_partner.py:0 #, python-format msgid "Impersonation successful" -msgstr "" +msgstr "Imitazione riuscita" #. module: fastapi_auth_partner #: model:ir.model.fields,help:fastapi_auth_partner.field_auth_directory__cookie_duration msgid "In minute, default 525600 minutes => 1 year" -msgstr "" +msgstr "In minuti, predefinito minuti => 1 anno" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__is_auth_partner msgid "Is Auth Partner" -msgstr "" +msgstr "È partner autorizzazione" #. module: fastapi_auth_partner #: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form msgid "Label" -msgstr "" +msgstr "Etichetta" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate____last_update msgid "Last Modified on" -msgstr "" +msgstr "Ultima modifica il" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__write_uid msgid "Last Updated by" -msgstr "" +msgstr "Ultimo aggiornamento di" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__write_date msgid "Last Updated on" -msgstr "" +msgstr "Ultimo aggiornamento il" #. module: fastapi_auth_partner #: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.auth_partner_view_form msgid "Local Impersonate" -msgstr "" +msgstr "Imitazione locale" #. module: fastapi_auth_partner #. odoo-python #: code:addons/fastapi_auth_partner/routers/auth.py:0 #, python-format msgid "No cookie secret key defined" -msgstr "" +msgstr "Nessuna chiave segreta cookie definita" #. module: fastapi_auth_partner #. odoo-python #: code:addons/fastapi_auth_partner/models/auth_partner.py:0 #, python-format msgid "Only admin can impersonate locally" -msgstr "" +msgstr "Solo l'amministratore può imitare localmente" #. module: fastapi_auth_partner #: model:ir.ui.menu,name:fastapi_auth_partner.auth_partner_menu msgid "Partner" -msgstr "" +msgstr "Partner" #. module: fastapi_auth_partner #: model:ir.model.fields.selection,name:fastapi_auth_partner.selection__fastapi_endpoint__demo_auth_method__auth_partner msgid "Partner Auth" -msgstr "" +msgstr "Autorizzazione partner" #. module: fastapi_auth_partner #: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form msgid "Please choose an endpoint:" -msgstr "" +msgstr "Scegliere un endpoint:" #. module: fastapi_auth_partner #. odoo-python #: code:addons/fastapi_auth_partner/models/auth_partner.py:0 #, python-format msgid "Please install base_future_response for local impersonate to work" -msgstr "" +msgstr "Installare base_future_response per far funzionare l'imitazione locale" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__public_api_url msgid "Public Api Url" -msgstr "" +msgstr "URL API pubblico" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__public_url msgid "Public Url" -msgstr "" +msgstr "URL pubblico" #. module: fastapi_auth_partner #: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.auth_directory_view_form msgid "Regenerate cookie secret key" -msgstr "" +msgstr "Rigenera chiave segreta cookie" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__sliding_session msgid "Sliding Session" -msgstr "" +msgstr "Sessione scorrevole" #. module: fastapi_auth_partner #: model:ir.model.fields,help:fastapi_auth_partner.field_fastapi_endpoint__is_auth_partner msgid "Technical field to know if the auth method is partner" -msgstr "" +msgstr "Campo tecnico per sapere se il metodo di autorizzazione è partner" #. module: fastapi_auth_partner #: model:ir.model.fields,help:fastapi_auth_partner.field_fastapi_endpoint__public_api_url @@ -230,6 +232,11 @@ msgid "" "This URL is used in impersonation to set the cookie on the right API domain if you use a reverse proxy to serve the API.\n" "Defaults to the public_url if not set or the odoo url if not set either." msgstr "" +"URL pubblico dell'API.\n" +"Questo URL viene utilizzato nell'imitazione per impostare il cookie sul " +"dominio API corretto se si utilizza un reverse proxy per servire l'API.\n" +"Il valore predefinito è public_url se non impostato, oppure l'URL di Odoo se " +"non impostato." #. module: fastapi_auth_partner #: model:ir.model.fields,help:fastapi_auth_partner.field_fastapi_endpoint__public_url @@ -238,21 +245,26 @@ msgid "" "This URL is used for the impersonation final redirect. And can also be used in the mail template to construct links.\n" "Default to the public_api_url if not set or the odoo url if not set either." msgstr "" +"URL pubblico del sito.\n" +"Questo URL viene utilizzato per il reindirizzamento finale dell'imitazione. " +"Può anche essere utilizzato nel modello di posta per creare link.\n" +"Impostato di default su public_api_url se non impostato, oppure su Odoo URL " +"se non impostato." #. module: fastapi_auth_partner #: model:ir.model.fields,help:fastapi_auth_partner.field_auth_directory__cookie_secret_key msgid "The secret key used to sign the cookie" -msgstr "" +msgstr "La chiave segreta usata per firmare il cookie" #. module: fastapi_auth_partner #: model:ir.model,name:fastapi_auth_partner.model_wizard_auth_partner_impersonate msgid "Wizard Partner Auth Impersonate" -msgstr "" +msgstr "Procedura guidata imitazione autorizzazione partner" #. module: fastapi_auth_partner #: model:ir.model,name:fastapi_auth_partner.model_wizard_auth_partner_reset_password msgid "Wizard Partner Auth Reset Password" -msgstr "" +msgstr "Procedura guidata reset password autorizzazione partner" #. module: fastapi_auth_partner #. odoo-python @@ -262,3 +274,5 @@ msgid "" "You are now impersonating %s\n" "%%s" msgstr "" +"Osa si sta imitando %s\n" +"%%s" From 20f685d54677ad45aa5a0ffc453588c6d3af5421 Mon Sep 17 00:00:00 2001 From: mymage Date: Wed, 10 Dec 2025 09:37:03 +0000 Subject: [PATCH 6/9] Translated using Weblate (Italian) Currently translated at 100.0% (45 of 45 strings) Translation: rest-framework-16.0/rest-framework-16.0-fastapi_auth_partner Translate-URL: https://translation.odoo-community.org/projects/rest-framework-16-0/rest-framework-16-0-fastapi_auth_partner/it/ --- fastapi_auth_partner/i18n/it.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fastapi_auth_partner/i18n/it.po b/fastapi_auth_partner/i18n/it.po index 6add7ce35..91674ad11 100644 --- a/fastapi_auth_partner/i18n/it.po +++ b/fastapi_auth_partner/i18n/it.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 16.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2025-06-04 16:26+0000\n" +"PO-Revision-Date: 2025-12-10 11:42+0000\n" "Last-Translator: mymage \n" "Language-Team: none\n" "Language: it\n" @@ -93,7 +93,7 @@ msgstr "Endpoint" #. module: fastapi_auth_partner #: model:ir.model,name:fastapi_auth_partner.model_fastapi_endpoint msgid "FastAPI Endpoint" -msgstr "Endopoint FastAPI" +msgstr "Endpoint FastAPI" #. module: fastapi_auth_partner #: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__fastapi_endpoint_ids From 66261abc4f4a977a72b64f4926439e8ad2450def Mon Sep 17 00:00:00 2001 From: Florian Mounier Date: Tue, 13 Jan 2026 11:18:11 +0100 Subject: [PATCH 7/9] [IMP] fastapi_auth_partner: pre-commit auto fixes --- fastapi_auth_partner/README.rst | 88 +++++++++---------- .../demo/fastapi_endpoint_demo.xml | 31 ++++--- fastapi_auth_partner/dependencies.py | 8 +- .../models/fastapi_endpoint.py | 3 +- fastapi_auth_partner/pyproject.toml | 3 + fastapi_auth_partner/readme/CONTRIBUTORS.md | 3 + fastapi_auth_partner/readme/CONTRIBUTORS.rst | 4 - fastapi_auth_partner/readme/DESCRIPTION.md | 3 + fastapi_auth_partner/readme/DESCRIPTION.rst | 2 - fastapi_auth_partner/readme/USAGE.md | 55 ++++++++++++ fastapi_auth_partner/readme/USAGE.rst | 52 ----------- fastapi_auth_partner/routers/auth.py | 2 +- fastapi_auth_partner/security/res_group.xml | 2 - .../static/description/index.html | 44 +++++----- .../tests/test_fastapi_auth_partner_demo.py | 2 +- .../wizard_auth_partner_impersonate_view.xml | 32 ++++--- .../wizard_auth_partner_reset_password.py | 2 +- ...izard_auth_partner_reset_password_view.xml | 2 - requirements.txt | 1 + 19 files changed, 170 insertions(+), 169 deletions(-) create mode 100644 fastapi_auth_partner/pyproject.toml create mode 100644 fastapi_auth_partner/readme/CONTRIBUTORS.md delete mode 100644 fastapi_auth_partner/readme/CONTRIBUTORS.rst create mode 100644 fastapi_auth_partner/readme/DESCRIPTION.md delete mode 100644 fastapi_auth_partner/readme/DESCRIPTION.rst create mode 100644 fastapi_auth_partner/readme/USAGE.md delete mode 100644 fastapi_auth_partner/readme/USAGE.rst diff --git a/fastapi_auth_partner/README.rst b/fastapi_auth_partner/README.rst index fc8471c13..6e652714e 100644 --- a/fastapi_auth_partner/README.rst +++ b/fastapi_auth_partner/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - ==================== Fastapi Auth Partner ==================== @@ -17,23 +13,24 @@ Fastapi Auth Partner .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Frest--framework-lightgray.png?logo=github - :target: https://github.com/OCA/rest-framework/tree/16.0/fastapi_auth_partner + :target: https://github.com/OCA/rest-framework/tree/18.0/fastapi_auth_partner :alt: OCA/rest-framework .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/rest-framework-16-0/rest-framework-16-0-fastapi_auth_partner + :target: https://translation.odoo-community.org/projects/rest-framework-18-0/rest-framework-18-0-fastapi_auth_partner :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/rest-framework&target_branch=16.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/rest-framework&target_branch=18.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| -This module is the FastAPI implementation of `auth_partner <../auth_partner>`_ -it provides all the routes to manage the authentication of partners. +This module is the FastAPI implementation of +`auth_partner <../auth_partner>`__ it provides all the routes to manage +the authentication of partners. **Table of contents** @@ -43,37 +40,39 @@ it provides all the routes to manage the authentication of partners. Usage ===== -First you have to add the auth router to your FastAPI endpoint and the authentication dependency to your app dependencies: +First you have to add the auth router to your FastAPI endpoint and the +authentication dependency to your app dependencies: -.. code-block:: python +.. code:: python - from odoo.addons.fastapi import dependencies - from odoo.addons.fastapi_auth_partner.dependencies import ( - auth_partner_authenticated_partner, - ) - from odoo.addons.fastapi_auth_partner.routers.auth import auth_router + from odoo.addons.fastapi import dependencies + from odoo.addons.fastapi_auth_partner.dependencies import ( + auth_partner_authenticated_partner, + ) + from odoo.addons.fastapi_auth_partner.routers.auth import auth_router - class FastapiEndpoint(models.Model): - _inherit = "fastapi.endpoint" + class FastapiEndpoint(models.Model): + _inherit = "fastapi.endpoint" - def _get_fastapi_routers(self): - if self.app == "myapp": - return [ - auth_router, - ] - return super()._get_fastapi_routers() + def _get_fastapi_routers(self): + if self.app == "myapp": + return [ + auth_router, + ] + return super()._get_fastapi_routers() - def _get_app_dependencies_overrides(self): - res = super()._get_app_dependencies_overrides() - if self.app == "myapp": - res.update( - { - dependencies.authenticated_partner_impl: auth_partner_authenticated_partner, - } - ) - return res + def _get_app_dependencies_overrides(self): + res = super()._get_app_dependencies_overrides() + if self.app == "myapp": + res.update( + { + dependencies.authenticated_partner_impl: auth_partner_authenticated_partner, + } + ) + return res -Next you can manage your authenticable partners and directories in the Odoo interface: +Next you can manage your authenticable partners and directories in the +Odoo interface: FastAPI > Authentication > Partner @@ -81,7 +80,8 @@ and FastAPI > Authentication > Directory -Next you must set the directory used for the authentication in the FastAPI endpoint: +Next you must set the directory used for the authentication in the +FastAPI endpoint: FastAPI > FastAPI Endpoint > myapp > Directory @@ -102,7 +102,7 @@ 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 `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -110,20 +110,20 @@ Credits ======= Authors -~~~~~~~ +------- * Akretion Contributors -~~~~~~~~~~~~ +------------ -* `Akretion `_: +- `Akretion `__: - * Sébastien Beau - * Florian Mounier + - Sébastien Beau + - Florian Mounier Maintainers -~~~~~~~~~~~ +----------- This module is maintained by the OCA. @@ -135,6 +135,6 @@ 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. -This module is part of the `OCA/rest-framework `_ project on GitHub. +This module is part of the `OCA/rest-framework `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fastapi_auth_partner/demo/fastapi_endpoint_demo.xml b/fastapi_auth_partner/demo/fastapi_endpoint_demo.xml index bf017151c..ff53c707c 100644 --- a/fastapi_auth_partner/demo/fastapi_endpoint_demo.xml +++ b/fastapi_auth_partner/demo/fastapi_endpoint_demo.xml @@ -1,8 +1,8 @@ - - Fastapi Auth Partner Demo Endpoint - + Fastapi Auth Partner Demo Endpoint + - demo - /fastapi_auth_partner_demo - auth_partner - - - https://api.example.com/ - https://www.example.com/ - + demo + /fastapi_auth_partner_demo + auth_partner + + + https://api.example.com/ + https://www.example.com/ + - - - + + + diff --git a/fastapi_auth_partner/dependencies.py b/fastapi_auth_partner/dependencies.py index 5d336e59c..9b5c7b51c 100644 --- a/fastapi_auth_partner/dependencies.py +++ b/fastapi_auth_partner/dependencies.py @@ -4,7 +4,7 @@ import logging import sys -from typing import Any, Dict, Union +from typing import Any from itsdangerous import URLSafeTimedSerializer from starlette.status import HTTP_401_UNAUTHORIZED @@ -20,12 +20,12 @@ if sys.version_info >= (3, 9): from typing import Annotated else: - from typing_extensions import Annotated + from typing import Annotated _logger = logging.getLogger(__name__) -Payload = Dict[str, Any] +Payload = dict[str, Any] class AuthPartner: @@ -41,7 +41,7 @@ def __call__( Depends(odoo_env), ], endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)], - fastapi_auth_partner: Annotated[Union[str, None], Cookie()] = None, + fastapi_auth_partner: Annotated[str | None, Cookie()] = None, ) -> Partner: if not fastapi_auth_partner and self.allow_unauthenticated: return env["res.partner"].with_user(env.ref("base.public_user")).browse() diff --git a/fastapi_auth_partner/models/fastapi_endpoint.py b/fastapi_auth_partner/models/fastapi_endpoint.py index cab4f80c5..8fa9491ba 100644 --- a/fastapi_auth_partner/models/fastapi_endpoint.py +++ b/fastapi_auth_partner/models/fastapi_endpoint.py @@ -3,7 +3,6 @@ # @author Florian Mounier # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from typing import List from odoo import fields, models @@ -44,7 +43,7 @@ class FastapiEndpoint(models.Model): "Default to the public_api_url if not set or the odoo url if not set either." ) - def _get_fastapi_routers(self) -> List[APIRouter]: + def _get_fastapi_routers(self) -> list[APIRouter]: routers = super()._get_fastapi_routers() if self.app == "demo" and self.demo_auth_method == "auth_partner": routers.append(auth_router) diff --git a/fastapi_auth_partner/pyproject.toml b/fastapi_auth_partner/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/fastapi_auth_partner/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/fastapi_auth_partner/readme/CONTRIBUTORS.md b/fastapi_auth_partner/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..6079ca504 --- /dev/null +++ b/fastapi_auth_partner/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +- [Akretion](https://www.akretion.com): + - Sébastien Beau + - Florian Mounier diff --git a/fastapi_auth_partner/readme/CONTRIBUTORS.rst b/fastapi_auth_partner/readme/CONTRIBUTORS.rst deleted file mode 100644 index bae3cc9a1..000000000 --- a/fastapi_auth_partner/readme/CONTRIBUTORS.rst +++ /dev/null @@ -1,4 +0,0 @@ -* `Akretion `_: - - * Sébastien Beau - * Florian Mounier diff --git a/fastapi_auth_partner/readme/DESCRIPTION.md b/fastapi_auth_partner/readme/DESCRIPTION.md new file mode 100644 index 000000000..8d4456e52 --- /dev/null +++ b/fastapi_auth_partner/readme/DESCRIPTION.md @@ -0,0 +1,3 @@ +This module is the FastAPI implementation of +[auth_partner](../auth_partner) it provides all the routes to manage the +authentication of partners. diff --git a/fastapi_auth_partner/readme/DESCRIPTION.rst b/fastapi_auth_partner/readme/DESCRIPTION.rst deleted file mode 100644 index e2fa8ca8d..000000000 --- a/fastapi_auth_partner/readme/DESCRIPTION.rst +++ /dev/null @@ -1,2 +0,0 @@ -This module is the FastAPI implementation of `auth_partner <../auth_partner>`_ -it provides all the routes to manage the authentication of partners. diff --git a/fastapi_auth_partner/readme/USAGE.md b/fastapi_auth_partner/readme/USAGE.md new file mode 100644 index 000000000..fc6bc9024 --- /dev/null +++ b/fastapi_auth_partner/readme/USAGE.md @@ -0,0 +1,55 @@ +First you have to add the auth router to your FastAPI endpoint and the +authentication dependency to your app dependencies: + +``` python +from odoo.addons.fastapi import dependencies +from odoo.addons.fastapi_auth_partner.dependencies import ( + auth_partner_authenticated_partner, +) +from odoo.addons.fastapi_auth_partner.routers.auth import auth_router + +class FastapiEndpoint(models.Model): + _inherit = "fastapi.endpoint" + + def _get_fastapi_routers(self): + if self.app == "myapp": + return [ + auth_router, + ] + return super()._get_fastapi_routers() + + def _get_app_dependencies_overrides(self): + res = super()._get_app_dependencies_overrides() + if self.app == "myapp": + res.update( + { + dependencies.authenticated_partner_impl: auth_partner_authenticated_partner, + } + ) + return res +``` + +Next you can manage your authenticable partners and directories in the +Odoo interface: + +FastAPI \> Authentication \> Partner + +and + +FastAPI \> Authentication \> Directory + +Next you must set the directory used for the authentication in the +FastAPI endpoint: + +FastAPI \> FastAPI Endpoint \> myapp \> Directory + +Then you can use the auth router to authenticate your requests: + +- POST /auth/register to register a partner +- POST /auth/login to authenticate a partner +- POST /auth/logout to unauthenticate a partner +- POST /auth/validate_email to validate a partner email +- POST /auth/request_reset_password to request a password reset +- POST /auth/set_password to set a new password +- GET /auth/profile to get the partner profile +- GET /auth/impersonate to impersonate a partner diff --git a/fastapi_auth_partner/readme/USAGE.rst b/fastapi_auth_partner/readme/USAGE.rst deleted file mode 100644 index 316655690..000000000 --- a/fastapi_auth_partner/readme/USAGE.rst +++ /dev/null @@ -1,52 +0,0 @@ -First you have to add the auth router to your FastAPI endpoint and the authentication dependency to your app dependencies: - -.. code-block:: python - - from odoo.addons.fastapi import dependencies - from odoo.addons.fastapi_auth_partner.dependencies import ( - auth_partner_authenticated_partner, - ) - from odoo.addons.fastapi_auth_partner.routers.auth import auth_router - - class FastapiEndpoint(models.Model): - _inherit = "fastapi.endpoint" - - def _get_fastapi_routers(self): - if self.app == "myapp": - return [ - auth_router, - ] - return super()._get_fastapi_routers() - - def _get_app_dependencies_overrides(self): - res = super()._get_app_dependencies_overrides() - if self.app == "myapp": - res.update( - { - dependencies.authenticated_partner_impl: auth_partner_authenticated_partner, - } - ) - return res - -Next you can manage your authenticable partners and directories in the Odoo interface: - -FastAPI > Authentication > Partner - -and - -FastAPI > Authentication > Directory - -Next you must set the directory used for the authentication in the FastAPI endpoint: - -FastAPI > FastAPI Endpoint > myapp > Directory - -Then you can use the auth router to authenticate your requests: - -- POST /auth/register to register a partner -- POST /auth/login to authenticate a partner -- POST /auth/logout to unauthenticate a partner -- POST /auth/validate_email to validate a partner email -- POST /auth/request_reset_password to request a password reset -- POST /auth/set_password to set a new password -- GET /auth/profile to get the partner profile -- GET /auth/impersonate to impersonate a partner diff --git a/fastapi_auth_partner/routers/auth.py b/fastapi_auth_partner/routers/auth.py index b06f4fcff..6d782f7da 100644 --- a/fastapi_auth_partner/routers/auth.py +++ b/fastapi_auth_partner/routers/auth.py @@ -8,7 +8,7 @@ if sys.version_info >= (3, 9): from typing import Annotated else: - from typing_extensions import Annotated + from typing import Annotated from datetime import datetime, timedelta, timezone diff --git a/fastapi_auth_partner/security/res_group.xml b/fastapi_auth_partner/security/res_group.xml index c7f87fb9e..e1879c2f3 100644 --- a/fastapi_auth_partner/security/res_group.xml +++ b/fastapi_auth_partner/security/res_group.xml @@ -1,6 +1,5 @@ - - diff --git a/fastapi_auth_partner/static/description/index.html b/fastapi_auth_partner/static/description/index.html index 46472f2f2..4ecf90032 100644 --- a/fastapi_auth_partner/static/description/index.html +++ b/fastapi_auth_partner/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +Fastapi Auth Partner -
+
+

Fastapi Auth Partner

- - -Odoo Community Association - -
-

Fastapi Auth Partner

-

Beta License: AGPL-3 OCA/rest-framework Translate me on Weblate Try me on Runboat

-

This module is the FastAPI implementation of auth_partner -it provides all the routes to manage the authentication of partners.

+

Beta License: AGPL-3 OCA/rest-framework Translate me on Weblate Try me on Runboat

+

This module is the FastAPI implementation of +auth_partner it provides all the routes to manage +the authentication of partners.

Table of contents

    @@ -391,8 +387,9 @@

    Fastapi Auth Partner

-

Usage

-

First you have to add the auth router to your FastAPI endpoint and the authentication dependency to your app dependencies:

+

Usage

+

First you have to add the auth router to your FastAPI endpoint and the +authentication dependency to your app dependencies:

 from odoo.addons.fastapi import dependencies
 from odoo.addons.fastapi_auth_partner.dependencies import (
@@ -420,11 +417,13 @@ 

Usage

) return res
-

Next you can manage your authenticable partners and directories in the Odoo interface:

+

Next you can manage your authenticable partners and directories in the +Odoo interface:

FastAPI > Authentication > Partner

and

FastAPI > Authentication > Directory

-

Next you must set the directory used for the authentication in the FastAPI endpoint:

+

Next you must set the directory used for the authentication in the +FastAPI endpoint:

FastAPI > FastAPI Endpoint > myapp > Directory

Then you can use the auth router to authenticate your requests:

    @@ -439,23 +438,23 @@

    Usage

-

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

+feedback.

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

-

Credits

+

Credits

-

Authors

+

Authors

  • Akretion
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -473,11 +472,10 @@

Maintainers

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.

-

This module is part of the OCA/rest-framework project on GitHub.

+

This module is part of the OCA/rest-framework project on GitHub.

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

-
diff --git a/fastapi_auth_partner/tests/test_fastapi_auth_partner_demo.py b/fastapi_auth_partner/tests/test_fastapi_auth_partner_demo.py index 0539cef17..9c7890b45 100644 --- a/fastapi_auth_partner/tests/test_fastapi_auth_partner_demo.py +++ b/fastapi_auth_partner/tests/test_fastapi_auth_partner_demo.py @@ -16,7 +16,7 @@ if sys.version_info >= (3, 9): from typing import Annotated else: - from typing_extensions import Annotated + from typing import Annotated from odoo.addons.fastapi_auth_partner.routers.auth import auth_router from odoo.addons.fastapi_auth_partner.schemas import AuthPartnerResponse diff --git a/fastapi_auth_partner/wizards/wizard_auth_partner_impersonate_view.xml b/fastapi_auth_partner/wizards/wizard_auth_partner_impersonate_view.xml index e9640c5df..77b9a7a75 100644 --- a/fastapi_auth_partner/wizards/wizard_auth_partner_impersonate_view.xml +++ b/fastapi_auth_partner/wizards/wizard_auth_partner_impersonate_view.xml @@ -1,28 +1,27 @@ - wizard.auth.partner.impersonate
Please choose an endpoint: - - - - + + + +
-
+