Initial commit: OCA Server Auth packages (29 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:06 +02:00
commit 3ed80311c4
1325 changed files with 127292 additions and 0 deletions

View file

@ -0,0 +1,47 @@
# Cross Connect Server
Odoo addon: cross_connect_server
## Installation
```bash
pip install odoo-bringout-oca-server-auth-cross_connect_server
```
## Dependencies
This addon depends on:
- extendable_fastapi
- server_environment
## Manifest Information
- **Name**: Cross Connect Server
- **Version**: 16.0.1.0.1
- **Category**: Tools
- **License**: AGPL-3
- **Installable**: True
## Source
Based on [OCA/server-auth](https://github.com/OCA/server-auth) branch 16.0, addon `cross_connect_server`.
## 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,114 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association
====================
Cross Connect Server
====================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:8008e84d0b38e668c79989062dede0d43276fbcbaa120effb1097b09f31e2968
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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%2Fserver--auth-lightgray.png?logo=github
:target: https://github.com/OCA/server-auth/tree/16.0/cross_connect_server
:alt: OCA/server-auth
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-auth-16-0/server-auth-16-0-cross_connect_server
: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/server-auth&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows other odoo instances, where the
``cross_connect_client`` module is installed and configured, users to
connect directly on this odoo instance.
**Table of contents**
.. contents::
:local:
Usage
=====
First of all after installing the module, you need to configure a
fastapi endpoint.
In order to do that, you need to go to the menu
``FastAPI > FastAPI Endpoints`` and create a new endpoint for the client
to connect to.
Fill the fields with the endpoint's information :
- App: ``cross_connect``
- Cross Connect Allowed Groups: The groups that will be allowed to be
selected for the clients groups.
Then for each client, you will have to add an entry in the
``Cross Connect Clients`` table.
An api key will be automatically generated for each client, this is the
key that you will have to provide to the client in order for them to
connect to the server. You will also have to choose the groups that this
client will be able to give to its users.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-auth/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/server-auth/issues/new?body=module:%20cross_connect_server%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
------------
- Florian Mounier florian.mounier@akretion.com
Maintainers
-----------
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
.. |maintainer-paradoxxxzero| image:: https://github.com/paradoxxxzero.png?size=40px
:target: https://github.com/paradoxxxzero
:alt: paradoxxxzero
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-paradoxxxzero|
This module is part of the `OCA/server-auth <https://github.com/OCA/server-auth/tree/16.0/cross_connect_server>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

@ -0,0 +1 @@
from . import models

View file

@ -0,0 +1,25 @@
# 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).
{
"name": "Cross Connect Server",
"version": "16.0.1.0.1",
"author": "Akretion, Odoo Community Association (OCA)",
"summary": "Cross Connect Server allows Cross Connect Client to connect to it.",
"category": "Tools",
"depends": ["extendable_fastapi", "server_environment"],
"website": "https://github.com/OCA/server-auth",
"data": [
"security/res_groups.xml",
"security/ir_model_access.xml",
"views/fastapi_endpoint_views.xml",
],
"maintainers": ["paradoxxxzero"],
"demo": [],
"installable": True,
"license": "AGPL-3",
"external_dependencies": {
"python": ["pyjwt"],
},
}

View file

@ -0,0 +1,9 @@
# 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 .models.cross_connect_client import CrossConnectClient
def authenticated_cross_connect_client() -> CrossConnectClient:
pass

View file

@ -0,0 +1,209 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * cross_connect_server
#
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: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__api_key
msgid "API Key"
msgstr "API ključ"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_fastapi_endpoint__app
msgid "App"
msgstr "Aplikacija"
#. module: cross_connect_server
#. odoo-python
#: code:addons/cross_connect_server/routers/cross_connect.py:0
#, python-format
msgid "Client not found"
msgstr "Klijent nije pronađen"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__create_uid
msgid "Created by"
msgstr "Kreirao"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__create_date
msgid "Created on"
msgstr "Kreirano"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__allowed_group_ids
#: model:ir.model.fields,field_description:cross_connect_server.field_fastapi_endpoint__cross_connect_allowed_group_ids
msgid "Cross Connect Allowed Groups"
msgstr "Dozvoljene grupe ukrštene konekcije"
#. module: cross_connect_server
#: model:ir.model,name:cross_connect_server.model_cross_connect_client
#: model:ir.model.fields,field_description:cross_connect_server.field_res_users__cross_connect_client_id
msgid "Cross Connect Client"
msgstr "Klijent ukrštene konekcije"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_res_users__cross_connect_client_user_id
msgid "Cross Connect Client User ID"
msgstr "ID korisnika klijenta ukrštene konekcije"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_fastapi_endpoint__cross_connect_client_ids
msgid "Cross Connect Clients"
msgstr "Klijenti ukrštene konekcije"
#. module: cross_connect_server
#: model_terms:ir.ui.view,arch_db:cross_connect_server.fastapi_endpoint_form_view
msgid "Cross Connect Configuration"
msgstr "Konfiguracija ukrštene konekcije"
#. module: cross_connect_server
#: model:ir.model.fields.selection,name:cross_connect_server.selection__fastapi_endpoint__app__cross_connect
msgid "Cross Connect Endpoint"
msgstr "Krajnja tačka ukrštene konekcije"
#. module: cross_connect_server
#: model:res.groups,name:cross_connect_server.group_cross_connect_manager
msgid "Cross Connect Manager"
msgstr "Upravljač ukrštene konekcije"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_fastapi_endpoint__cross_connect_secret_key
msgid "Cross Connect Secret Key"
msgstr "Tajna ključ ukrštene konekcije"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__user_count
msgid "Cross Connected User Count"
msgstr "Broj korisnika ukršteno povezanih"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__display_name
msgid "Display Name"
msgstr "Prikazani naziv"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__endpoint_id
msgid "Endpoint"
msgstr "Krajnja tačka"
#. module: cross_connect_server
#: model:ir.model,name:cross_connect_server.model_fastapi_endpoint
msgid "FastAPI Endpoint"
msgstr "FastAPI krajnja tačka"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__group_ids
msgid "Groups"
msgstr "Grupe"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__id
msgid "ID"
msgstr "ID"
#. module: cross_connect_server
#. odoo-python
#: code:addons/cross_connect_server/models/cross_connect_client.py:0
#: code:addons/cross_connect_server/models/cross_connect_client.py:0
#, python-format
msgid "Invalid Token"
msgstr "Neispravan token"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client____last_update
msgid "Last Modified on"
msgstr "Zadnje mijenjano"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__write_uid
msgid "Last Updated by"
msgstr "Zadnji ažurirao"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__write_date
msgid "Last Updated on"
msgstr "Zadnje ažurirano"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__name
msgid "Name"
msgstr "Naziv:"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__server_env_defaults
msgid "Server Env Defaults"
msgstr "Zadane vrijednosti serverskog okruženja"
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__api_key
msgid "The API key to give to configure on the client."
msgstr "API ključ koji treba dati za konfiguriranje na klijentu."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_fastapi_endpoint__cross_connect_client_ids
msgid "The clients that can access this endpoint."
msgstr "Klijenti koji mogu pristupiti ovoj krajnjoj tački."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_res_users__cross_connect_client_id
msgid "The cross connect client that created this user."
msgstr "Klijent ukrštene konekcije koji je kreirao ovog korisnika."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__allowed_group_ids
#: model:ir.model.fields,help:cross_connect_server.field_fastapi_endpoint__cross_connect_allowed_group_ids
msgid "The groups that can access the cross connect clients of this endpoint."
msgstr "Grupe koje mogu pristupiti klijentima ukrštene konekcije ove krajnje tačke."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__group_ids
msgid "The groups that this client belongs to."
msgstr "Grupe kojima ovaj klijent pripada."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__user_count
msgid "The number of users created by this cross connection."
msgstr "Broj korisnika kreiranih ovom ukrštenom konekcijom."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_fastapi_endpoint__cross_connect_secret_key
msgid "The secret key used for cross connection."
msgstr "Tajni ključ korišten za ukrštenu konekciju."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_res_users__cross_connect_client_user_id
msgid "The user ID on the cross connect client."
msgstr "ID korisnika na klijentu ukrštene konekcije."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__user_ids
msgid "The users created by this cross connection."
msgstr "Korisnici kreirani ovom ukrštenom konekcijom."
#. module: cross_connect_server
#: model:ir.model,name:cross_connect_server.model_res_users
msgid "User"
msgstr "Korisnik"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__user_ids
msgid "Users"
msgstr "Korisnici"
#. module: cross_connect_server
#. odoo-python
#: code:addons/cross_connect_server/models/cross_connect_client.py:0
#, python-format
msgid "You are not allowed to access this endpoint."
msgstr "Nemate dozvolu pristupa ovoj krajnjoj tački."

View file

@ -0,0 +1,209 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * cross_connect_server
#
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: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__api_key
msgid "API Key"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_fastapi_endpoint__app
msgid "App"
msgstr ""
#. module: cross_connect_server
#. odoo-python
#: code:addons/cross_connect_server/routers/cross_connect.py:0
#, python-format
msgid "Client not found"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__create_uid
msgid "Created by"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__create_date
msgid "Created on"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__allowed_group_ids
#: model:ir.model.fields,field_description:cross_connect_server.field_fastapi_endpoint__cross_connect_allowed_group_ids
msgid "Cross Connect Allowed Groups"
msgstr ""
#. module: cross_connect_server
#: model:ir.model,name:cross_connect_server.model_cross_connect_client
#: model:ir.model.fields,field_description:cross_connect_server.field_res_users__cross_connect_client_id
msgid "Cross Connect Client"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_res_users__cross_connect_client_user_id
msgid "Cross Connect Client User ID"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_fastapi_endpoint__cross_connect_client_ids
msgid "Cross Connect Clients"
msgstr ""
#. module: cross_connect_server
#: model_terms:ir.ui.view,arch_db:cross_connect_server.fastapi_endpoint_form_view
msgid "Cross Connect Configuration"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields.selection,name:cross_connect_server.selection__fastapi_endpoint__app__cross_connect
msgid "Cross Connect Endpoint"
msgstr ""
#. module: cross_connect_server
#: model:res.groups,name:cross_connect_server.group_cross_connect_manager
msgid "Cross Connect Manager"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_fastapi_endpoint__cross_connect_secret_key
msgid "Cross Connect Secret Key"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__user_count
msgid "Cross Connected User Count"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__display_name
msgid "Display Name"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__endpoint_id
msgid "Endpoint"
msgstr ""
#. module: cross_connect_server
#: model:ir.model,name:cross_connect_server.model_fastapi_endpoint
msgid "FastAPI Endpoint"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__group_ids
msgid "Groups"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__id
msgid "ID"
msgstr ""
#. module: cross_connect_server
#. odoo-python
#: code:addons/cross_connect_server/models/cross_connect_client.py:0
#: code:addons/cross_connect_server/models/cross_connect_client.py:0
#, python-format
msgid "Invalid Token"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client____last_update
msgid "Last Modified on"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__write_uid
msgid "Last Updated by"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__write_date
msgid "Last Updated on"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__name
msgid "Name"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__server_env_defaults
msgid "Server Env Defaults"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__api_key
msgid "The API key to give to configure on the client."
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_fastapi_endpoint__cross_connect_client_ids
msgid "The clients that can access this endpoint."
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_res_users__cross_connect_client_id
msgid "The cross connect client that created this user."
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__allowed_group_ids
#: model:ir.model.fields,help:cross_connect_server.field_fastapi_endpoint__cross_connect_allowed_group_ids
msgid "The groups that can access the cross connect clients of this endpoint."
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__group_ids
msgid "The groups that this client belongs to."
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__user_count
msgid "The number of users created by this cross connection."
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_fastapi_endpoint__cross_connect_secret_key
msgid "The secret key used for cross connection."
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_res_users__cross_connect_client_user_id
msgid "The user ID on the cross connect client."
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__user_ids
msgid "The users created by this cross connection."
msgstr ""
#. module: cross_connect_server
#: model:ir.model,name:cross_connect_server.model_res_users
msgid "User"
msgstr ""
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__user_ids
msgid "Users"
msgstr ""
#. module: cross_connect_server
#. odoo-python
#: code:addons/cross_connect_server/models/cross_connect_client.py:0
#, python-format
msgid "You are not allowed to access this endpoint."
msgstr ""

View file

@ -0,0 +1,213 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * cross_connect_server
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-05-20 10: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: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__api_key
msgid "API Key"
msgstr "Chiave API"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_fastapi_endpoint__app
msgid "App"
msgstr "Applicazione"
#. module: cross_connect_server
#. odoo-python
#: code:addons/cross_connect_server/routers/cross_connect.py:0
#, python-format
msgid "Client not found"
msgstr "Client non trovato"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__create_uid
msgid "Created by"
msgstr "Creato da"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__create_date
msgid "Created on"
msgstr "Creato il"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__allowed_group_ids
#: model:ir.model.fields,field_description:cross_connect_server.field_fastapi_endpoint__cross_connect_allowed_group_ids
msgid "Cross Connect Allowed Groups"
msgstr "Gruppi abilitati cross-connect"
#. module: cross_connect_server
#: model:ir.model,name:cross_connect_server.model_cross_connect_client
#: model:ir.model.fields,field_description:cross_connect_server.field_res_users__cross_connect_client_id
msgid "Cross Connect Client"
msgstr "Client cross-connect"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_res_users__cross_connect_client_user_id
msgid "Cross Connect Client User ID"
msgstr "ID utente cliente cross-connect"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_fastapi_endpoint__cross_connect_client_ids
msgid "Cross Connect Clients"
msgstr "Client cross-connect"
#. module: cross_connect_server
#: model_terms:ir.ui.view,arch_db:cross_connect_server.fastapi_endpoint_form_view
msgid "Cross Connect Configuration"
msgstr "Configurazione cross-connect"
#. module: cross_connect_server
#: model:ir.model.fields.selection,name:cross_connect_server.selection__fastapi_endpoint__app__cross_connect
msgid "Cross Connect Endpoint"
msgstr "Endopoint cross-connect"
#. module: cross_connect_server
#: model:res.groups,name:cross_connect_server.group_cross_connect_manager
msgid "Cross Connect Manager"
msgstr "Gestore cross-connect"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_fastapi_endpoint__cross_connect_secret_key
msgid "Cross Connect Secret Key"
msgstr "Chiave segreta cross-connect"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__user_count
msgid "Cross Connected User Count"
msgstr "Conteggio utenti cross-connect"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__display_name
msgid "Display Name"
msgstr "Nome visualizzato"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__endpoint_id
msgid "Endpoint"
msgstr "Endpoint"
#. module: cross_connect_server
#: model:ir.model,name:cross_connect_server.model_fastapi_endpoint
msgid "FastAPI Endpoint"
msgstr "Endpoint FastAPI"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__group_ids
msgid "Groups"
msgstr "Gruppi"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__id
msgid "ID"
msgstr "ID"
#. module: cross_connect_server
#. odoo-python
#: code:addons/cross_connect_server/models/cross_connect_client.py:0
#: code:addons/cross_connect_server/models/cross_connect_client.py:0
#, python-format
msgid "Invalid Token"
msgstr "Token non valido"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client____last_update
msgid "Last Modified on"
msgstr "Ultima modifica il"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__write_uid
msgid "Last Updated by"
msgstr "Ultimo aggiornamento di"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__write_date
msgid "Last Updated on"
msgstr "Ultimo aggiornamento il"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__name
msgid "Name"
msgstr "Nome"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__server_env_defaults
msgid "Server Env Defaults"
msgstr "Predefiniti ambiente server"
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__api_key
msgid "The API key to give to configure on the client."
msgstr "Chiave API da fornire per configurare il client."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_fastapi_endpoint__cross_connect_client_ids
msgid "The clients that can access this endpoint."
msgstr "I client che possono accedere questo endpoint."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_res_users__cross_connect_client_id
msgid "The cross connect client that created this user."
msgstr "Il client cross-connect che ha creato questo utente."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__allowed_group_ids
#: model:ir.model.fields,help:cross_connect_server.field_fastapi_endpoint__cross_connect_allowed_group_ids
msgid "The groups that can access the cross connect clients of this endpoint."
msgstr ""
"I gruppi che possono accedere i client cross-connect di questo endpoint."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__group_ids
msgid "The groups that this client belongs to."
msgstr "I gruppi a cui appartiene questo client."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__user_count
msgid "The number of users created by this cross connection."
msgstr "Il numero di utenti creati da questa cross-connect."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_fastapi_endpoint__cross_connect_secret_key
msgid "The secret key used for cross connection."
msgstr "La chiave segreta usata per la cross-connect."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_res_users__cross_connect_client_user_id
msgid "The user ID on the cross connect client."
msgstr "L'ID utente nel client cross-connect."
#. module: cross_connect_server
#: model:ir.model.fields,help:cross_connect_server.field_cross_connect_client__user_ids
msgid "The users created by this cross connection."
msgstr "Gli utenti creati da questa cross-connect."
#. module: cross_connect_server
#: model:ir.model,name:cross_connect_server.model_res_users
msgid "User"
msgstr "Utente"
#. module: cross_connect_server
#: model:ir.model.fields,field_description:cross_connect_server.field_cross_connect_client__user_ids
msgid "Users"
msgstr "Utenti"
#. module: cross_connect_server
#. odoo-python
#: code:addons/cross_connect_server/models/cross_connect_client.py:0
#, python-format
msgid "You are not allowed to access this endpoint."
msgstr "Non si è autorizzati ad accedere a questo endpoint."

View file

@ -0,0 +1,3 @@
from . import cross_connect_client
from . import fastapi_endpoint
from . import res_users

View file

@ -0,0 +1,120 @@
# 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 datetime import datetime, timedelta, timezone
from secrets import token_urlsafe
import jwt
from odoo import _, api, fields, models
from odoo.exceptions import AccessDenied
class CrossConnectClient(models.Model):
_name = "cross.connect.client"
_description = "Cross Connect Client"
_inherit = "server.env.mixin"
name = fields.Char(required=True)
endpoint_id = fields.Many2one(
"fastapi.endpoint",
required=True,
string="Endpoint",
)
api_key = fields.Char(
required=True,
string="API Key",
help="The API key to give to configure on the client.",
default=lambda self: self._generate_api_key(),
)
allowed_group_ids = fields.Many2many(
related="endpoint_id.cross_connect_allowed_group_ids",
)
group_ids = fields.Many2many(
"res.groups",
string="Groups",
help="The groups that this client belongs to.",
domain="[('id', 'in', allowed_group_ids)]",
)
user_ids = fields.One2many(
"res.users",
"cross_connect_client_id",
string="Users",
help="The users created by this cross connection.",
)
user_count = fields.Integer(
compute="_compute_user_count",
string="Cross Connected User Count",
help="The number of users created by this cross connection.",
)
@api.model
def _generate_api_key(self):
# generate random ~64 chars secret key
return token_urlsafe(64)
@api.depends("user_ids")
def _compute_user_count(self):
for record in self:
record.user_count = len(record.user_ids)
def _request_access(self, access_request):
# check groups
groups = self.env["res.groups"].browse(access_request.groups)
if groups - self.group_ids or not groups.exists():
raise AccessDenied(_("You are not allowed to access this endpoint."))
user = self.user_ids.filtered(
lambda u: u.cross_connect_client_user_id == access_request.id
)
vals = {
"login": f"{self.id}_{access_request.id}_{access_request.login}",
"email": access_request.email,
"name": access_request.name,
"lang": access_request.lang,
"groups_id": [(6, 0, groups.ids)],
"cross_connect_client_id": self.id,
"cross_connect_client_user_id": access_request.id,
}
# Create user if not exists
if not user:
user = (
self.env["res.users"].with_context(no_reset_password=True).create(vals)
)
else:
user.write(vals)
return jwt.encode(
{
"exp": datetime.now(tz=timezone.utc) + timedelta(minutes=2),
"aud": str(self.id),
"id": user.id,
"redirect_url": access_request.redirect_url or "/web",
},
self.endpoint_id.cross_connect_secret_key,
algorithm="HS256",
)
def _log_from_token(self, token):
try:
obj = jwt.decode(
token,
self.endpoint_id.cross_connect_secret_key,
audience=str(self.id),
options={"require": ["exp", "aud", "id"]},
algorithms=["HS256"],
)
except jwt.PyJWTError as e:
raise AccessDenied(_("Invalid Token")) from e
user = self.env["res.users"].browse(obj["id"])
if not user:
raise AccessDenied(_("Invalid Token"))
return user, obj["redirect_url"]

View file

@ -0,0 +1,104 @@
# 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 secrets import token_urlsafe
from typing import Annotated, Callable, Dict, List
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import APIKeyHeader
from odoo import api, fields, models
from odoo.api import Environment
from odoo.addons.fastapi.dependencies import fastapi_endpoint, odoo_env
from ..dependencies import authenticated_cross_connect_client
from ..routers import cross_connect_router
from .cross_connect_client import CrossConnectClient
class FastapiEndpoint(models.Model):
_inherit = "fastapi.endpoint"
app = fields.Selection(
selection_add=[("cross_connect", "Cross Connect Endpoint")],
ondelete={"cross_connect": "cascade"},
)
cross_connect_client_ids = fields.One2many(
"cross.connect.client",
"endpoint_id",
string="Cross Connect Clients",
help="The clients that can access this endpoint.",
)
cross_connect_allowed_group_ids = fields.Many2many(
"res.groups",
string="Cross Connect Allowed Groups",
help="The groups that can access the cross connect clients of this endpoint.",
)
cross_connect_secret_key = fields.Char(
help="The secret key used for cross connection.",
required=True,
default=lambda self: self._generate_secret_key(),
)
@api.model
def _generate_secret_key(self):
# generate random ~64 chars secret key
return token_urlsafe(64)
def _get_fastapi_routers(self) -> List[APIRouter]:
routers = super()._get_fastapi_routers()
if self.app == "cross_connect":
routers += [cross_connect_router]
return routers
def _get_app_dependencies_overrides(self) -> Dict[Callable, Callable]:
overrides = super()._get_app_dependencies_overrides()
if self.app == "cross_connect":
overrides[
authenticated_cross_connect_client
] = api_key_based_authenticated_cross_connect_client
return overrides
def _get_routing_info(self):
if self.app == "cross_connect":
# Force to not save the HTTP session for the login to work correctly
self.save_http_session = False
return super()._get_routing_info()
@property
def _server_env_fields(self):
return {"cross_connect_secret_key": {}}
def api_key_based_authenticated_cross_connect_client(
api_key: Annotated[
str,
Depends(
APIKeyHeader(
name="api-key",
description="Cross Connect Client API key.",
)
),
],
fastapi_endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)],
env: Annotated[Environment, Depends(odoo_env)],
) -> CrossConnectClient:
cross_connect_client = (
env["cross.connect.client"]
.sudo()
.search(
[("api_key", "=", api_key), ("endpoint_id", "=", fastapi_endpoint.id)],
limit=1,
)
)
if not cross_connect_client:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect API Key"
)
return cross_connect_client

View file

@ -0,0 +1,19 @@
# 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 ResUsers(models.Model):
_inherit = "res.users"
cross_connect_client_id = fields.Many2one(
"cross.connect.client",
string="Cross Connect Client",
help="The cross connect client that created this user.",
)
cross_connect_client_user_id = fields.Integer(
string="Cross Connect Client User ID",
help="The user ID on the cross connect client.",
)

View file

@ -0,0 +1 @@
- Florian Mounier <florian.mounier@akretion.com>

View file

@ -0,0 +1,2 @@
This module allows other odoo instances, where the `cross_connect_client` module is
installed and configured, users to connect directly on this odoo instance.

View file

@ -0,0 +1,17 @@
First of all after installing the module, you need to configure a fastapi endpoint.
In order to do that, you need to go to the menu `FastAPI > FastAPI Endpoints` and create
a new endpoint for the client to connect to.
Fill the fields with the endpoint's information :
- App: `cross_connect`
- Cross Connect Allowed Groups: The groups that will be allowed to be selected for the
clients groups.
Then for each client, you will have to add an entry in the `Cross Connect Clients`
table.
An api key will be automatically generated for each client, this is the key that you
will have to provide to the client in order for them to connect to the server. You will
also have to choose the groups that this client will be able to give to its users.

View file

@ -0,0 +1 @@
from .cross_connect import cross_connect_router

View file

@ -0,0 +1,78 @@
# 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 typing import Annotated
from fastapi import APIRouter, Depends
from fastapi.responses import RedirectResponse
from odoo import _, api
from odoo.exceptions import MissingError
from odoo.http import SESSION_LIFETIME, root
from odoo.addons.fastapi.dependencies import odoo_env
from ..dependencies import authenticated_cross_connect_client
from ..models.cross_connect_client import CrossConnectClient
from ..schemas import AccessRequest, AccessResponse, SyncResponse
cross_connect_router = APIRouter(tags=["Cross Connect"])
@cross_connect_router.get("/cross_connect/sync")
async def sync(
cross_connect_client: Annotated[
CrossConnectClient, Depends(authenticated_cross_connect_client)
],
) -> SyncResponse:
"""Send back to client sync information."""
return SyncResponse.from_groups(cross_connect_client.group_ids)
@cross_connect_router.post("/cross_connect/access")
async def access(
cross_connect_client: Annotated[
CrossConnectClient, Depends(authenticated_cross_connect_client)
],
access_request: AccessRequest,
) -> AccessResponse:
"""Send back to client a token."""
return AccessResponse.from_params(
client_id=cross_connect_client.id,
token=cross_connect_client.sudo()._request_access(access_request),
)
@cross_connect_router.get("/cross_connect/login/{client_id}/{token}")
async def login(
client_id: int,
token: str,
env: Annotated[api.Environment, Depends(odoo_env)],
) -> RedirectResponse:
"""Log user and redirect to odoo index."""
cross_connect_client = env["cross.connect.client"].sudo().browse(client_id)
if not cross_connect_client:
raise MissingError(_("Client not found"))
user, redirect_url = cross_connect_client.sudo()._log_from_token(token)
user = user.with_user(user)
user._update_last_login()
env = env(user=user.id)
# Create a odoo session
session = root.session_store.new()
session.db = env.cr.dbname
session.uid = user.id
session.login = user.login
session.context = dict(env["res.users"].context_get())
session.session_token = user._compute_session_token(session.sid)
root.session_store.save(session)
# Redirect after login
response = RedirectResponse(url=redirect_url)
response.set_cookie(
"session_id",
session.sid,
httponly=True,
max_age=SESSION_LIFETIME,
)
return response

View file

@ -0,0 +1,47 @@
# 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 extendable_pydantic import StrictExtendableBaseModel
class CrossConnectGroup(StrictExtendableBaseModel):
id: int
name: str
comment: str | None = None
@classmethod
def from_group(cls, group):
return cls.model_construct(
id=group.id,
name=group.full_name,
comment=group.comment or None,
)
class SyncResponse(StrictExtendableBaseModel):
groups: list[CrossConnectGroup]
@classmethod
def from_groups(cls, groups):
return cls.model_construct(
groups=[CrossConnectGroup.from_group(group) for group in groups]
)
class AccessRequest(StrictExtendableBaseModel, extra="ignore"):
id: int
name: str
login: str
email: str
lang: str
groups: list[int]
redirect_url: str = None
class AccessResponse(StrictExtendableBaseModel):
client_id: int
token: str
@classmethod
def from_params(cls, token, client_id):
return cls.model_construct(token=token, client_id=client_id)

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
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).
-->
<odoo>
<record id="access_cross_connect_client_manager" model="ir.model.access">
<field name="name">Cross Connect Client: Manager RW access</field>
<field name="model_id" ref="model_cross_connect_client" />
<field name="group_id" ref="group_cross_connect_manager" />
<field name="perm_read" eval="True" />
<field name="perm_write" eval="True" />
<field name="perm_create" eval="True" />
<field name="perm_unlink" eval="True" />
</record>
</odoo>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
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).
-->
<odoo>
<record id="group_cross_connect_manager" model="res.groups">
<field name="name">Cross Connect Manager</field>
<field
name="users"
eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"
/>
</record>
</odoo>

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -0,0 +1,454 @@
<!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="cross-connect-server">
<h1>Cross Connect Server</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:8008e84d0b38e668c79989062dede0d43276fbcbaa120effb1097b09f31e2968
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<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/server-auth/tree/16.0/cross_connect_server"><img alt="OCA/server-auth" src="https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/server-auth-16-0/server-auth-16-0-cross_connect_server"><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/server-auth&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 allows other odoo instances, where the
<tt class="docutils literal">cross_connect_client</tt> module is installed and configured, users to
connect directly on this odoo instance.</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 of all after installing the module, you need to configure a
fastapi endpoint.</p>
<p>In order to do that, you need to go to the menu
<tt class="docutils literal">FastAPI &gt; FastAPI Endpoints</tt> and create a new endpoint for the client
to connect to.</p>
<p>Fill the fields with the endpoints information :</p>
<ul class="simple">
<li>App: <tt class="docutils literal">cross_connect</tt></li>
<li>Cross Connect Allowed Groups: The groups that will be allowed to be
selected for the clients groups.</li>
</ul>
<p>Then for each client, you will have to add an entry in the
<tt class="docutils literal">Cross Connect Clients</tt> table.</p>
<p>An api key will be automatically generated for each client, this is the
key that you will have to provide to the client in order for them to
connect to the server. You will also have to choose the groups that this
client will be able to give to its users.</p>
</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/server-auth/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/server-auth/issues/new?body=module:%20cross_connect_server%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>Florian Mounier <a class="reference external" href="mailto:florian.mounier&#64;akretion.com">florian.mounier&#64;akretion.com</a></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>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external image-reference" href="https://github.com/paradoxxxzero"><img alt="paradoxxxzero" src="https://github.com/paradoxxxzero.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/server-auth/tree/16.0/cross_connect_server">OCA/server-auth</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 @@
from . import test_cross_connect_server

View file

@ -0,0 +1,375 @@
# 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.http import root
from odoo.tests.common import RecordCapturer, tagged
from odoo.addons.extendable_fastapi.tests.common import FastAPITransactionCase
from ..routers import cross_connect_router
@tagged("post_install", "-at_install")
class TestCrossConnectServer(FastAPITransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.endpoint = cls.env["fastapi.endpoint"].create(
{
"name": "Cross Connect Server Endpoint",
"root_path": "/api",
"app": "cross_connect",
}
)
cls.available_groups = (
cls.env.ref("base.group_user")
| cls.env.ref("fastapi.group_fastapi_user")
| cls.env.ref("fastapi.group_fastapi_manager")
)
cls.endpoint.cross_connect_allowed_group_ids = cls.available_groups
cls.client = cls.env["cross.connect.client"].create(
{
"name": "Test Client",
"endpoint_id": cls.endpoint.id,
"api_key": "server-api-key",
"group_ids": [
(
6,
0,
(
cls.available_groups
- cls.env.ref("fastapi.group_fastapi_manager")
).ids,
)
],
}
)
cls.other_client = cls.env["cross.connect.client"].create(
{
"name": "Other Test Client",
"endpoint_id": cls.endpoint.id,
"api_key": "other-server-api-key",
"group_ids": [
(
6,
0,
(cls.available_groups - cls.env.ref("base.group_user")).ids,
)
],
}
)
cls.endpoint_user = cls.env["res.users"].create(
{
"name": "FastAPI Endpoint User",
"login": "fastapi_endpoint_user",
"groups_id": [
(6, 0, [cls.env.ref("fastapi.group_fastapi_endpoint_runner").id])
],
}
)
cls.endpoint._handle_registry_sync(cls.endpoint.ids)
cls.default_fastapi_running_user = cls.endpoint_user
cls.default_fastapi_router = cross_connect_router
cls.default_fastapi_app = cls.endpoint._get_app()
cls.default_fastapi_dependency_overrides = (
cls.default_fastapi_app.dependency_overrides
)
cls.default_fastapi_app.exception_handlers = {}
def test_base(self):
self.assertTrue(self.endpoint.cross_connect_secret_key)
self.assertEqual(len(self.endpoint.cross_connect_client_ids), 2)
self.assertFalse(self.endpoint.save_http_session)
self.assertFalse(self.client.user_ids)
def test_sync_ok(self):
with self._create_test_client() as test_client:
response = test_client.get(
"/cross_connect/sync", headers={"api-key": "server-api-key"}
)
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.json(),
{
"groups": [
{
"id": self.env.ref("base.group_user").id,
"name": "User types / Internal User",
"comment": None,
},
{
"id": self.env.ref("fastapi.group_fastapi_user").id,
"name": "FastAPI / User",
"comment": None,
},
]
},
)
def test_sync_other(self):
with self._create_test_client() as test_client:
response = test_client.get(
"/cross_connect/sync", headers={"api-key": "other-server-api-key"}
)
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.json(),
{
"groups": [
{
"id": self.env.ref("fastapi.group_fastapi_manager").id,
"name": "FastAPI / Administrator",
"comment": None,
},
{
"id": self.env.ref("fastapi.group_fastapi_user").id,
"name": "FastAPI / User",
"comment": None,
},
]
},
)
def test_sync_401(self):
with self._create_test_client(raise_server_exceptions=False) as test_client:
response = test_client.get(
"/cross_connect/sync", headers={"api-key": "wrong-api-key"}
)
self.assertEqual(response.status_code, 401)
def test_access_ok(self):
with RecordCapturer(self.env["res.users"], []) as rc:
with self._create_test_client() as test_client:
response = test_client.post(
"/cross_connect/access",
headers={"api-key": "server-api-key"},
json={
"id": 12,
"name": "Client User",
"login": "user@client.example.org",
"email": "user@client.example.org",
"lang": "en_US",
"groups": [
self.env.ref("base.group_user").id,
],
},
)
self.assertEqual(response.status_code, 200)
json = response.json()
self.assertEqual(json["client_id"], self.client.id)
self.assertTrue(json["token"])
self.assertEqual(len(rc.records), 1)
new_user = rc.records[0]
self.assertEqual(new_user.name, "Client User")
self.assertEqual(new_user.login, f"{self.client.id}_12_user@client.example.org")
self.assertEqual(new_user.email, "user@client.example.org")
self.assertEqual(new_user.lang, "en_US")
self.assertEqual(new_user.cross_connect_client_id.id, self.client.id)
self.assertEqual(new_user.cross_connect_client_user_id, 12)
self.assertIn(
self.env.ref("base.group_user"),
new_user.groups_id,
)
self.assertNotIn(self.env.ref("fastapi.group_fastapi_user"), new_user.groups_id)
self.assertNotIn(
self.env.ref("fastapi.group_fastapi_manager"), new_user.groups_id
)
def test_access_401(self):
with RecordCapturer(self.env["res.users"], []) as rc:
with self._create_test_client(raise_server_exceptions=False) as test_client:
response = test_client.post(
"/cross_connect/access",
headers={"api-key": "wrong-api-key"},
json={
"id": 12,
"name": "Client User",
"login": "user@client.example.org",
"email": "user@client.example.org",
"lang": "en_US",
"groups": [
self.env.ref("base.group_user").id,
],
},
)
self.assertEqual(response.status_code, 401)
self.assertEqual(len(rc.records), 0)
def test_access_wrong_groups(self):
with RecordCapturer(self.env["res.users"], []) as rc:
with self._create_test_client(raise_server_exceptions=False) as test_client:
response = test_client.post(
"/cross_connect/access",
headers={"api-key": "wrong-api-key"},
json={
"id": 12,
"name": "Client User",
"login": "user@client.example.org",
"email": "user@client.example.org",
"lang": "en_US",
"groups": [
self.env.ref("fastapi.group_fastapi_manager").id,
],
},
)
self.assertEqual(response.status_code, 401)
self.assertEqual(len(rc.records), 0)
def test_access_existing(self):
with RecordCapturer(self.env["res.users"], []) as rc:
with self._create_test_client() as test_client:
response = test_client.post(
"/cross_connect/access",
headers={"api-key": "server-api-key"},
json={
"id": 12,
"name": "Client User",
"login": "user@client.example.org",
"email": "user@client.example.org",
"lang": "en_US",
"groups": [
self.env.ref("base.group_user").id,
],
},
)
self.assertEqual(response.status_code, 200)
with RecordCapturer(self.env["res.users"], []) as rc2:
with self._create_test_client() as test_client:
response = test_client.post(
"/cross_connect/access",
headers={"api-key": "server-api-key"},
json={
"id": 12,
"name": "Client User2",
"login": "user2@client.example.org",
"email": "user2@client.example.org",
"lang": "en_US",
"groups": [
self.env.ref("fastapi.group_fastapi_user").id,
],
},
)
self.assertEqual(response.status_code, 200)
json = response.json()
self.assertEqual(json["client_id"], self.client.id)
self.assertTrue(json["token"])
self.assertEqual(len(rc.records), 1)
self.assertEqual(len(rc2.records), 0)
new_user = rc.records[0]
self.assertEqual(new_user.name, "Client User2")
self.assertEqual(
new_user.login, f"{self.client.id}_12_user2@client.example.org"
)
self.assertEqual(new_user.email, "user2@client.example.org")
self.assertEqual(new_user.lang, "en_US")
self.assertIn(self.env.ref("fastapi.group_fastapi_user"), new_user.groups_id)
self.assertNotIn(
self.env.ref("fastapi.group_fastapi_manager"), new_user.groups_id
)
def test_login_ok(self):
with RecordCapturer(self.env["res.users"], []) as rc:
with self._create_test_client() as test_client:
response = test_client.post(
"/cross_connect/access",
headers={"api-key": "server-api-key"},
json={
"id": 12,
"name": "Client User",
"login": "user@client.example.org",
"email": "user@client.example.org",
"lang": "en_US",
"groups": [
self.env.ref("base.group_user").id,
],
},
)
self.assertEqual(response.status_code, 200)
new_user = rc.records[0]
json = response.json()
with self._create_test_client() as test_client:
response = test_client.get(
f"/cross_connect/login/{json['client_id']}/{json['token']}",
follow_redirects=False,
)
self.assertEqual(response.status_code, 307)
self.assertEqual(response.headers["location"], "/web")
self.assertIn("session_id", response.cookies)
self.assertEqual(
root.session_store.get(response.cookies["session_id"]).get("uid"),
new_user.id,
)
def test_login_wrong_client(self):
with self._create_test_client() as test_client:
response = test_client.post(
"/cross_connect/access",
headers={"api-key": "server-api-key"},
json={
"id": 12,
"name": "Client User",
"login": "user@client.example.org",
"email": "user@client.example.org",
"lang": "en_US",
"groups": [
self.env.ref("base.group_user").id,
],
},
)
self.assertEqual(response.status_code, 200)
json = response.json()
with self._create_test_client(raise_server_exceptions=False) as test_client:
response = test_client.get(
f"/cross_connect/login/{self.other_client.id}/{json['token']}",
follow_redirects=False,
)
self.assertEqual(response.status_code, 403)
def test_login_wrong_token(self):
with self._create_test_client() as test_client:
response = test_client.post(
"/cross_connect/access",
headers={"api-key": "server-api-key"},
json={
"id": 12,
"name": "Client User",
"login": "user@client.example.org",
"email": "user@client.example.org",
"lang": "en_US",
"groups": [
self.env.ref("base.group_user").id,
],
},
)
self.assertEqual(response.status_code, 200)
json = response.json()
with self._create_test_client(raise_server_exceptions=False) as test_client:
response = test_client.get(
f"/cross_connect/login/{json['client_id']}/wrong-token",
follow_redirects=False,
)
self.assertEqual(response.status_code, 403)

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
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).
-->
<odoo>
<record model="ir.ui.view" id="fastapi_endpoint_form_view">
<field name="model">fastapi.endpoint</field>
<field name="inherit_id" ref="fastapi.fastapi_endpoint_form_view" />
<field name="arch" type="xml">
<field name="save_http_session" position="attributes">
<attribute name="attrs">
{'invisible': [('app', '==', 'cross_connect')]}
</attribute>
</field>
<span name="configuration" position="after">
<group
name="cross_connect"
title="Cross Connect Configuration"
attrs="{'invisible': [('app', '!=', 'cross_connect')]}"
groups="cross_connect_server.group_cross_connect_manager"
>
<field
name="cross_connect_allowed_group_ids"
widget="many2many_tags"
options="{'open': True}"
/>
<field name="cross_connect_client_ids">
<tree editable="bottom">
<field name="name" />
<field name="api_key" />
<field name="allowed_group_ids" invisible="True" />
<field
name="group_ids"
widget="many2many_tags"
options="{'no_create': True}"
/>
<field name="user_count" />
</tree>
</field>
</group>
</span>
</field>
</record>
</odoo>

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 Cross_connect_server Module - cross_connect_server
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 cross_connect_server. 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)
- server_environment

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 cross_connect_server or install in UI.

View file

@ -0,0 +1,7 @@
# Install
```bash
pip install odoo-bringout-oca-server-auth-cross_connect_server"
# or
uv pip install odoo-bringout-oca-server-auth-cross_connect_server"
```

View file

@ -0,0 +1,15 @@
# Models
Detected core models and extensions in cross_connect_server.
```mermaid
classDiagram
class cross_connect_client
class fastapi_endpoint
class res_users
class server_env_mixin
```
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: cross_connect_server. Provides features documented in upstream Odoo 16 under this addon.
- Source: OCA/OCB 16.0, addon cross_connect_server
- License: LGPL-3

View file

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

View file

@ -0,0 +1,73 @@
# Security
Access control and security definitions in cross_connect_server.
## Access Control Lists (ACLs)
Model access permissions defined in:
- **[bosnian_translations.json](../bosnian_translations.json)**
- 50 model access rules
- **[bosnian_translations_output.json](../bosnian_translations_output.json)**
- 444 model access rules
- **[CHANGELOG.md](../CHANGELOG.md)**
- 132 model access rules
- **[delete_odoo_addons.sh](../delete_odoo_addons.sh)**
- 44 model access rules
- **[doc](../doc)**
- **[docker](../docker)**
- **[input](../input)**
- **[nix](../nix)**
- **[odoo.conf](../odoo.conf)**
- 58 model access rules
- **[odoo_packages_bez_l10n.txt](../odoo_packages_bez_l10n.txt)**
- 1947 model access rules
- **[odoo_packages_bringout.txt](../odoo_packages_bringout.txt)**
- 1947 model access rules
- **[odoo_packages.txt](../odoo_packages.txt)**
- 2085 model access rules
- **[output](../output)**
- **[packages](../packages)**
- **[PACKAGES.md](../PACKAGES.md)**
- 298 model access rules
- **[README.md](../README.md)**
- 338 model access rules
- **[scripts](../scripts)**
- **[temp](../temp)**
- **[TRANSLATION_BS_SUMMARY.md](../TRANSLATION_BS_SUMMARY.md)**
- 146 model access rules
- **[verify_deletions.sh](../verify_deletions.sh)**
- 55 model access rules
## Record Rules
Row-level security rules defined in:
## Security Groups & Configuration
Security groups and permissions defined in:
- **[ir_model_access.xml](../cross_connect_server/security/ir_model_access.xml)**
- **[res_groups.xml](../cross_connect_server/security/res_groups.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.xml](../cross_connect_server/security/ir_model_access.xml)**
- Security groups, categories, and XML-based rules
- **[res_groups.xml](../cross_connect_server/security/res_groups.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 cross_connect_server
```

View file

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

View file

@ -0,0 +1,43 @@
[project]
name = "odoo-bringout-oca-server-auth-cross_connect_server"
version = "16.0.0"
description = "Cross Connect Server - Cross Connect Server allows Cross Connect Client to connect to it."
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-server-auth-extendable_fastapi>=16.0.0",
"odoo-bringout-oca-server-auth-server_environment>=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 = ["cross_connect_server"]
[tool.rye]
managed = true
dev-dependencies = [
"pytest>=8.4.1",
]