Initial commit: OCA Technical packages (595 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:03 +02:00
commit 2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions

View file

@ -0,0 +1,47 @@
# Fastapi Auth Partner
Odoo addon: fastapi_auth_partner
## Installation
```bash
pip install odoo-bringout-oca-rest-framework-fastapi_auth_partner
```
## Dependencies
This addon depends on:
- extendable_fastapi
- auth_partner
## Manifest Information
- **Name**: Fastapi Auth Partner
- **Version**: 16.0.1.0.0
- **Category**: N/A
- **License**: AGPL-3
- **Installable**: False
## Source
Based on [OCA/rest-framework](https://github.com/OCA/rest-framework) branch 16.0, addon `fastapi_auth_partner`.
## License
This package maintains the original AGPL-3 license from the upstream Odoo project.
## Documentation
- Overview: doc/OVERVIEW.md
- Architecture: doc/ARCHITECTURE.md
- Models: doc/MODELS.md
- Controllers: doc/CONTROLLERS.md
- Wizards: doc/WIZARDS.md
- Reports: doc/REPORTS.md
- Security: doc/SECURITY.md
- Install: doc/INSTALL.md
- Usage: doc/USAGE.md
- Configuration: doc/CONFIGURATION.md
- Dependencies: doc/DEPENDENCIES.md
- Troubleshooting: doc/TROUBLESHOOTING.md
- FAQ: doc/FAQ.md

View file

@ -0,0 +1,32 @@
# Architecture
```mermaid
flowchart TD
U[Users] -->|HTTP| V[Views and QWeb Templates]
V --> C[Controllers]
V --> W[Wizards Transient Models]
C --> M[Models and ORM]
W --> M
M --> R[Reports]
DX[Data XML] --> M
S[Security ACLs and Groups] -. enforces .-> M
subgraph Fastapi_auth_partner Module - fastapi_auth_partner
direction LR
M:::layer
W:::layer
C:::layer
V:::layer
R:::layer
S:::layer
DX:::layer
end
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
```
Notes
- Views include tree/form/kanban templates and report templates.
- Controllers provide website/portal routes when present.
- Wizards are UI flows implemented with `models.TransientModel`.
- Data XML loads data/demo records; Security defines groups and access.

View file

@ -0,0 +1,3 @@
# Configuration
Refer to Odoo settings for fastapi_auth_partner. Configure related models, access rights, and options as needed.

View file

@ -0,0 +1,3 @@
# Controllers
This module does not define custom HTTP controllers.

View file

@ -0,0 +1,6 @@
# Dependencies
This addon depends on:
- [extendable_fastapi](../../odoo-bringout-oca-rest-framework-extendable_fastapi)
- [auth_partner](../../odoo-bringout-oca-rest-framework-auth_partner)

View file

@ -0,0 +1,4 @@
# FAQ
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
- Q: How to enable? A: Start server with --addon fastapi_auth_partner or install in UI.

View file

@ -0,0 +1,7 @@
# Install
```bash
pip install odoo-bringout-oca-rest-framework-fastapi_auth_partner"
# or
uv pip install odoo-bringout-oca-rest-framework-fastapi_auth_partner"
```

View file

@ -0,0 +1,14 @@
# Models
Detected core models and extensions in fastapi_auth_partner.
```mermaid
classDiagram
class auth_directory
class auth_partner
class fastapi_endpoint
```
Notes
- Classes show model technical names; fields omitted for brevity.
- Items listed under _inherit are extensions of existing models.

View file

@ -0,0 +1,6 @@
# Overview
Packaged Odoo addon: fastapi_auth_partner. Provides features documented in upstream Odoo 16 under this addon.
- Source: OCA/OCB 16.0, addon fastapi_auth_partner
- License: LGPL-3

View file

@ -0,0 +1,3 @@
# Reports
This module does not define custom reports.

View file

@ -0,0 +1,42 @@
# Security
Access control and security definitions in fastapi_auth_partner.
## Access Control Lists (ACLs)
Model access permissions defined in:
- **[ir.model.access.csv](../fastapi_auth_partner/security/ir.model.access.csv)**
- 1 model access rules
## Record Rules
Row-level security rules defined in:
## Security Groups & Configuration
Security groups and permissions defined in:
- **[res_group.xml](../fastapi_auth_partner/security/res_group.xml)**
- 1 security groups defined
```mermaid
graph TB
subgraph "Security Layers"
A[Users] --> B[Groups]
B --> C[Access Control Lists]
C --> D[Models]
B --> E[Record Rules]
E --> F[Individual Records]
end
```
Security files overview:
- **[ir.model.access.csv](../fastapi_auth_partner/security/ir.model.access.csv)**
- Model access permissions (CRUD rights)
- **[res_group.xml](../fastapi_auth_partner/security/res_group.xml)**
- Security groups, categories, and XML-based rules
Notes
- Access Control Lists define which groups can access which models
- Record Rules provide row-level security (filter records by user/group)
- Security groups organize users and define permission sets
- All security is enforced at the ORM level by Odoo

View file

@ -0,0 +1,5 @@
# Troubleshooting
- Ensure Python and Odoo environment matches repo guidance.
- Check database connectivity and logs if startup fails.
- Validate that dependent addons listed in DEPENDENCIES.md are installed.

View file

@ -0,0 +1,7 @@
# Usage
Start Odoo including this addon (from repo root):
```bash
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon fastapi_auth_partner
```

View file

@ -0,0 +1,3 @@
# Wizards
This module does not include UI wizards.

View file

@ -0,0 +1,140 @@
.. 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
====================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! 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/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
: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 <https://github.com/OCA/rest-framework/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 <https://github.com/OCA/rest-framework/issues/new?body=module:%20fastapi_auth_partner%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Akretion
Contributors
~~~~~~~~~~~~
* `Akretion <https://www.akretion.com>`_:
* 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 <https://github.com/OCA/rest-framework/tree/16.0/fastapi_auth_partner>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

@ -0,0 +1,4 @@
from . import models
from . import routers
from . import schemas
from . import wizards

View file

@ -0,0 +1,32 @@
# Copyright 2024 Akretion (http://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# @author Florian Mounier <florian.mounier@akretion.com>
# 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"],
},
}

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="fastapi.endpoint" id="fastapi_endpoint_demo">
<field name="name">Fastapi Auth Partner Demo Endpoint</field>
<field
name="description"
><![CDATA[
# A Dummy FastApi Partner Auth Demo
This demo endpoint has been created by inhering from "fastapi.endpoint", registering
a new app into the app selection field and implementing the `_get_fastapi_routers`
methods. See documentation to learn more about how to create a new app.
]]></field>
<field name="app">demo</field>
<field name="root_path">/fastapi_auth_partner_demo</field>
<field name="demo_auth_method">auth_partner</field>
<field name="user_id" ref="fastapi.my_demo_app_user" />
<field name="directory_id" ref="auth_partner.demo_directory" />
<field name="public_api_url">https://api.example.com/</field>
<field name="public_url">https://www.example.com/</field>
</record>
<record id="fastapi.my_demo_app_user" model="res.users">
<field name="groups_id" eval="[(4, ref('auth_partner.group_auth_partner_api'))]" />
</record>
</odoo>

View file

@ -0,0 +1,74 @@
# Copyright 2024 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# 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)

View file

@ -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 "Aplikacija"
#. module: fastapi_auth_partner
#: model:ir.model,name:fastapi_auth_partner.model_auth_directory
msgid "Auth Directory"
msgstr "Auth direktorij"
#. 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 "Auth partner"
#. module: fastapi_auth_partner
#: model:ir.ui.menu,name:fastapi_auth_partner.fastapi_auth
msgid "Authentication"
msgstr "Autentifikacija"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__demo_auth_method
msgid "Authentication method"
msgstr "Metoda autentifikacije"
#. module: fastapi_auth_partner
#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form
msgid "Cancel"
msgstr "Otkaži"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__cookie_duration
msgid "Cookie Duration"
msgstr "Trajanje kolačića"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__cookie_secret_key
msgid "Cookie Secret Key"
msgstr "Tajni ključ kolačića"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__create_uid
msgid "Created by"
msgstr "Kreirao"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__create_date
msgid "Created on"
msgstr "Kreirano"
#. module: fastapi_auth_partner
#: model:ir.model.fields.selection,name:fastapi_auth_partner.selection__fastapi_endpoint__app__demo
msgid "Demo Endpoint"
msgstr "Demo krajnja točka"
#. 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 "Imenik"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__display_name
msgid "Display Name"
msgstr "Prikazani naziv"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_auth_service__endpoint_id
msgid "Endpoint"
msgstr "Krajnja točka"
#. module: fastapi_auth_partner
#: model:ir.model,name:fastapi_auth_partner.model_fastapi_endpoint
msgid "FastAPI Endpoint"
msgstr "FastAPI krajnja točka"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__fastapi_endpoint_ids
msgid "FastAPI Endpoints"
msgstr "FastAPI krajnje točke"
#. module: fastapi_auth_partner
#: model:ir.model,name:fastapi_auth_partner.model_fastapi_auth_service
msgid "Fastapi Auth Service"
msgstr "Fastapi auth servis"
#. 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 "Fastapi krajnja točka"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__id
msgid "ID"
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 "Oponašanje"
#. module: fastapi_auth_partner
#. odoo-python
#: code:addons/fastapi_auth_partner/models/auth_partner.py:0
#, python-format
msgid "Impersonation successful"
msgstr "Oponašanje uspješno"
#. 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 "U minutama, zadano 525600 minuta => 1 godina"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__is_auth_partner
msgid "Is Auth Partner"
msgstr "Je auth partner"
#. module: fastapi_auth_partner
#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.wizard_auth_partner_impersonate_view_form
msgid "Label"
msgstr "Opis"
#. 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 "Zadnje mijenjano"
#. 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 "Zadnji ažurirao"
#. 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 "Zadnje ažurirano"
#. module: fastapi_auth_partner
#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.auth_partner_view_form
msgid "Local Impersonate"
msgstr "Lokalno oponašanje"
#. module: fastapi_auth_partner
#. odoo-python
#: code:addons/fastapi_auth_partner/routers/auth.py:0
#, python-format
msgid "No cookie secret key defined"
msgstr "Nema definiranog tajnog ključa kolačića"
#. 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 "Samo administrator može lokalno oponašati"
#. module: fastapi_auth_partner
#: model:ir.ui.menu,name:fastapi_auth_partner.auth_partner_menu
msgid "Partner"
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 "Partner auth"
#. 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 "Molimo odaberite krajnju točku:"
#. 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 "Molimo instalirajte base_future_response da lokalno oponašanje radi"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__public_api_url
msgid "Public Api Url"
msgstr "Javni API URL"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_endpoint__public_url
msgid "Public Url"
msgstr "Javni URL"
#. module: fastapi_auth_partner
#: model_terms:ir.ui.view,arch_db:fastapi_auth_partner.auth_directory_view_form
msgid "Regenerate cookie secret key"
msgstr "Regeneriraj tajni ključ kolačića"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__sliding_session
msgid "Sliding Session"
msgstr "Klizna sesija"
#. 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 "Tehničko polje za poznavanje je li auth metoda partner"
#. 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 "Tajni ključ koji se koristi za potpisivanje kolačića"
#. module: fastapi_auth_partner
#: model:ir.model,name:fastapi_auth_partner.model_wizard_auth_partner_impersonate
msgid "Wizard Partner Auth Impersonate"
msgstr "Čarobnjak partnera za auth oponašanje"
#. module: fastapi_auth_partner
#: model:ir.model,name:fastapi_auth_partner.model_wizard_auth_partner_reset_password
msgid "Wizard Partner Auth Reset Password"
msgstr "Čarobnjak partnera za auth resetiranje lozinke"
#. 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 ""

View file

@ -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 ""

View file

@ -0,0 +1,278 @@
# 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"
"PO-Revision-Date: 2025-06-04 16:26+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\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 "Applicazione"
#. module: fastapi_auth_partner
#: model:ir.model,name:fastapi_auth_partner.model_auth_directory
msgid "Auth Directory"
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 "Partner autorizzazione"
#. module: fastapi_auth_partner
#: model:ir.ui.menu,name:fastapi_auth_partner.fastapi_auth
msgid "Authentication"
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 "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 "Annulla"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_auth_directory__cookie_duration
msgid "Cookie Duration"
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 "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 "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 "Creato il"
#. module: fastapi_auth_partner
#: model:ir.model.fields.selection,name:fastapi_auth_partner.selection__fastapi_endpoint__app__demo
msgid "Demo Endpoint"
msgstr "Endpoint esempio"
#. 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 "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 "Nome visualizzato"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_fastapi_auth_service__endpoint_id
msgid "Endpoint"
msgstr "Endpoint"
#. module: fastapi_auth_partner
#: model:ir.model,name:fastapi_auth_partner.model_fastapi_endpoint
msgid "FastAPI Endpoint"
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 "Endpoint FastAPI"
#. module: fastapi_auth_partner
#: model:ir.model,name:fastapi_auth_partner.model_fastapi_auth_service
msgid "Fastapi Auth Service"
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 "Endopoint FastAPI"
#. module: fastapi_auth_partner
#: model:ir.model.fields,field_description:fastapi_auth_partner.field_wizard_auth_partner_impersonate__id
msgid "ID"
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 "Imita"
#. module: fastapi_auth_partner
#. odoo-python
#: code:addons/fastapi_auth_partner/models/auth_partner.py:0
#, python-format
msgid "Impersonation successful"
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 "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 "È 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 "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 "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 "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 "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 "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 "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 "Solo l'amministratore può imitare localmente"
#. module: fastapi_auth_partner
#: model:ir.ui.menu,name:fastapi_auth_partner.auth_partner_menu
msgid "Partner"
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 "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 "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 "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 "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 "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 "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 "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 "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
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 ""
"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
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 ""
"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 "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 "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 "Procedura guidata reset password autorizzazione partner"
#. 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 ""
"Osa si sta imitando %s\n"
"%%s"

View file

@ -0,0 +1,3 @@
from . import auth_directory
from . import auth_partner
from . import fastapi_endpoint

View file

@ -0,0 +1,51 @@
# Copyright 2024 Akretion (http://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# @author Florian Mounier <florian.mounier@akretion.com>
# 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": {},
}

View file

@ -0,0 +1,82 @@
# Copyright 2024 Akretion (http://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# @author Florian Mounier <florian.mounier@akretion.com>
# 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)

View file

@ -0,0 +1,55 @@
# Copyright 2024 Akretion (http://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# @author Florian Mounier <florian.mounier@akretion.com>
# 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()

View file

@ -0,0 +1,4 @@
* `Akretion <https://www.akretion.com>`_:
* Sébastien Beau
* Florian Mounier

View file

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

View file

@ -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

View file

@ -0,0 +1 @@
from .auth import auth_router

View file

@ -0,0 +1,252 @@
# Copyright 2024 Akretion (http://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# @author Florian Mounier <florian.mounier@akretion.com>
# 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)

View file

@ -0,0 +1,40 @@
# Copyright 2024 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# 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
)

View file

@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 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

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="auth_partner.group_auth_partner_api" model="res.groups">
<field
name="implied_ids"
eval="[
(4, ref('fastapi.group_fastapi_endpoint_runner')),
]"
/>
</record>
</odoo>

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -0,0 +1,483 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="fastapi-auth-partner">
<h1>Fastapi Auth Partner</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:2ebd9377ca7b035ab9fb0383513aacb5ca8645f69d5d85c171883b40b439017e
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/rest-framework/tree/16.0/fastapi_auth_partner"><img alt="OCA/rest-framework" src="https://img.shields.io/badge/github-OCA%2Frest--framework-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/rest-framework-16-0/rest-framework-16-0-fastapi_auth_partner"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/rest-framework&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module is the FastAPI implementation of <a class="reference external" href="../auth_partner">auth_partner</a>
it provides all the routes to manage the authentication of partners.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#usage" id="toc-entry-1">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-1">Usage</a></h2>
<p>First you have to add the auth router to your FastAPI endpoint and the authentication dependency to your app dependencies:</p>
<pre class="code python literal-block">
<span class="kn">from</span><span class="w"> </span><span class="nn">odoo.addons.fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">dependencies</span><span class="w">
</span><span class="kn">from</span><span class="w"> </span><span class="nn">odoo.addons.fastapi_auth_partner.dependencies</span><span class="w"> </span><span class="kn">import</span> <span class="p">(</span><span class="w">
</span> <span class="n">auth_partner_authenticated_partner</span><span class="p">,</span><span class="w">
</span><span class="p">)</span><span class="w">
</span><span class="kn">from</span><span class="w"> </span><span class="nn">odoo.addons.fastapi_auth_partner.routers.auth</span><span class="w"> </span><span class="kn">import</span> <span class="n">auth_router</span><span class="w">
</span><span class="k">class</span><span class="w"> </span><span class="nc">FastapiEndpoint</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span><span class="w">
</span> <span class="n">_inherit</span> <span class="o">=</span> <span class="s2">&quot;fastapi.endpoint&quot;</span><span class="w">
</span> <span class="k">def</span><span class="w"> </span><span class="nf">_get_fastapi_routers</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><span class="w">
</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">==</span> <span class="s2">&quot;myapp&quot;</span><span class="p">:</span><span class="w">
</span> <span class="k">return</span> <span class="p">[</span><span class="w">
</span> <span class="n">auth_router</span><span class="p">,</span><span class="w">
</span> <span class="p">]</span><span class="w">
</span> <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">_get_fastapi_routers</span><span class="p">()</span><span class="w">
</span> <span class="k">def</span><span class="w"> </span><span class="nf">_get_app_dependencies_overrides</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><span class="w">
</span> <span class="n">res</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">_get_app_dependencies_overrides</span><span class="p">()</span><span class="w">
</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">==</span> <span class="s2">&quot;myapp&quot;</span><span class="p">:</span><span class="w">
</span> <span class="n">res</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="w">
</span> <span class="p">{</span><span class="w">
</span> <span class="n">dependencies</span><span class="o">.</span><span class="n">authenticated_partner_impl</span><span class="p">:</span> <span class="n">auth_partner_authenticated_partner</span><span class="p">,</span><span class="w">
</span> <span class="p">}</span><span class="w">
</span> <span class="p">)</span><span class="w">
</span> <span class="k">return</span> <span class="n">res</span>
</pre>
<p>Next you can manage your authenticable partners and directories in the Odoo interface:</p>
<p>FastAPI &gt; Authentication &gt; Partner</p>
<p>and</p>
<p>FastAPI &gt; Authentication &gt; Directory</p>
<p>Next you must set the directory used for the authentication in the FastAPI endpoint:</p>
<p>FastAPI &gt; FastAPI Endpoint &gt; myapp &gt; Directory</p>
<p>Then you can use the auth router to authenticate your requests:</p>
<ul class="simple">
<li>POST /auth/register to register a partner</li>
<li>POST /auth/login to authenticate a partner</li>
<li>POST /auth/logout to unauthenticate a partner</li>
<li>POST /auth/validate_email to validate a partner email</li>
<li>POST /auth/request_reset_password to request a password reset</li>
<li>POST /auth/set_password to set a new password</li>
<li>GET /auth/profile to get the partner profile</li>
<li>GET /auth/impersonate to impersonate a partner</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/rest-framework/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/rest-framework/issues/new?body=module:%20fastapi_auth_partner%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-3">Credits</a></h2>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-4">Authors</a></h3>
<ul class="simple">
<li>Akretion</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-5">Contributors</a></h3>
<ul class="simple">
<li><a class="reference external" href="https://www.akretion.com">Akretion</a>:<ul>
<li>Sébastien Beau</li>
<li>Florian Mounier</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h3>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>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.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/rest-framework/tree/16.0/fastapi_auth_partner">OCA/rest-framework</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,2 @@
from . import test_auth
from . import test_fastapi_auth_partner_demo

View file

@ -0,0 +1,243 @@
# Copyright 2024 Akretion (http://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# 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)
)

View file

@ -0,0 +1,93 @@
# Copyright 2023 ACSONE SA/NV
# Copyright 2023 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# 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}
)

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="auth_directory_view_form" model="ir.ui.view">
<field name="model">auth.directory</field>
<field name="inherit_id" ref="auth_partner.auth_directory_view_form" />
<field name="arch" type="xml">
<header position="inside">
<button
name="action_regenerate_cookie_secret_key"
string="Regenerate cookie secret key"
type="object"
groups="base.group_system"
/>
</header>
<field name="secret_key" position="after">
<field name="cookie_secret_key" />
<field name="cookie_duration" />
<field name="sliding_session" />
</field>
</field>
</record>
<menuitem
id="auth_directory_menu"
parent="fastapi_auth"
sequence="10"
action="auth_partner.auth_directory_action"
/>
</odoo>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="auth_partner_view_form" model="ir.ui.view">
<field name="model">auth.partner</field>
<field name="inherit_id" ref="auth_partner.auth_partner_view_form" />
<field name="arch" type="xml">
<button name="impersonate" position="before">
<button
name="local_impersonate"
type="object"
string="Local Impersonate"
class="btn-secondary"
groups="base.group_no_one"
/>
</button>
</field>
</record>
<menuitem
id="fastapi_auth"
parent="fastapi.menu_fastapi_root"
sequence="50"
name="Authentication"
/>
<menuitem
id="auth_partner_menu"
parent="fastapi_auth"
sequence="10"
action="auth_partner.auth_partner_action"
/>
</odoo>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="fastapi_endpoint_view_form" model="ir.ui.view">
<field name="model">fastapi.endpoint</field>
<field name="inherit_id" ref="fastapi.fastapi_endpoint_form_view" />
<field name="arch" type="xml">
<span name="configuration" position="after">
<group name="Auth Configuration">
<field name="is_auth_partner" invisible="1" />
<field
name="directory_id"
attrs="{
'invisible': [('is_auth_partner', '=', False)],
'required': [('is_auth_partner', '=', True)]
}"
/>
</group>
</span>
<field name="openapi_url" position="after">
<field name="public_url" widget="url" />
<field name="public_api_url" widget="url" />
</field>
</field>
</record>
</odoo>

View file

@ -0,0 +1,2 @@
from . import wizard_auth_partner_impersonate
from . import wizard_auth_partner_reset_password

View file

@ -0,0 +1,29 @@
# Copyright 2024 Akretion (http://www.akretion.com).
# @author Florian Mounier <florian.mounier@akretion.com>
# 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()

View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="wizard_auth_partner_impersonate_view_form" model="ir.ui.view">
<field name="model">wizard.auth.partner.impersonate</field>
<field name="arch" type="xml">
<form string="Label">
Please choose an endpoint:
<group>
<field name="auth_partner_id" readonly="1" />
<field name="auth_directory_id" invisible="1" />
<field
name="fastapi_endpoint_id"
domain="[('directory_id', '=', auth_directory_id)]"
/>
</group>
<footer>
<button
name="action_impersonate"
string="Impersonate"
type="object"
class="oe_highlight"
/>
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="auth_partner_action_impersonate" model="ir.actions.act_window">
<field name="name">Impersonate</field>
<field name="res_model">wizard.auth.partner.impersonate</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field
name="groups_id"
eval="[(4, ref('auth_partner.group_auth_partner_manager'))]"
/>
</record>
</odoo>

View file

@ -0,0 +1,18 @@
# Copyright 2024 Akretion (https://www.akretion.com).
# @author Florian Mounier <florian.mounier@akretion.com>
# 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()

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="wizard_auth_partner_reset_password_view_form" model="ir.ui.view">
<field name="model">wizard.auth.partner.reset.password</field>
<field
name="inherit_id"
ref="auth_partner.wizard_auth_partner_reset_password_view_form"
/>
<field name="arch" type="xml">
<group position="inside">
<field name="fastapi_endpoint_id" />
</group>
</field>
</record>
</odoo>

View file

@ -0,0 +1,43 @@
[project]
name = "odoo-bringout-oca-rest-framework-fastapi_auth_partner"
version = "16.0.0"
description = "Fastapi Auth Partner - This provides an implementation of auth_partner for FastAPI"
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-rest-framework-extendable_fastapi>=16.0.0",
"odoo-bringout-oca-rest-framework-auth_partner>=16.0.0",
"requests>=2.25.1"
]
readme = "README.md"
requires-python = ">= 3.11"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Office/Business",
]
[project.urls]
homepage = "https://github.com/bringout/0"
repository = "https://github.com/bringout/0"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.metadata]
allow-direct-references = true
[tool.hatch.build.targets.wheel]
packages = ["fastapi_auth_partner"]
[tool.rye]
managed = true
dev-dependencies = [
"pytest>=8.4.1",
]