Skip to content

Commit c3e5a28

Browse files
committed
[ADD] cross_connect_client
1 parent 035093d commit c3e5a28

19 files changed

Lines changed: 1237 additions & 0 deletions

File tree

cross_connect_client/README.rst

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
====================
2+
Cross Connect Client
3+
====================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:f10ceaed1b91df49c3a9b4e8acdef3d412d7ce50105f6f1ca752630fc1559d8e
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
18+
:alt: License: AGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github
20+
:target: https://github.com/OCA/server-auth/tree/16.0/cross_connect_client
21+
:alt: OCA/server-auth
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/server-auth-16-0/server-auth-16-0-cross_connect_client
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-auth&target_branch=16.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
This module allows this odoo instance users to connect directly on
32+
another odoo instance where the module ``cross_connect_server`` is
33+
installed.
34+
35+
**Table of contents**
36+
37+
.. contents::
38+
:local:
39+
40+
Usage
41+
=====
42+
43+
First of all after installing the module, you need to configure the
44+
server connection.
45+
46+
In order to do that, you need to go to the menu
47+
``Settings > Technical > Cross Connect > Cross Connect Servers`` and
48+
create a new server to connect to.
49+
50+
Fill the fields with the server's information :
51+
52+
- Url: The api root path (e.g. ``https://my-remote-odoo.com/api``)
53+
- Api Key: The api-key from the ``cross_connect_server`` configuration
54+
55+
Then click on the ``Sync Cross Connection`` button to check if the
56+
connection is working and to sync the remote server's groups.
57+
58+
After that, you will have to affect the remote groups to the local users
59+
in order for them to be able to connect to the remote server.
60+
61+
Once an user has a remote group, a new top level menu will appear in the
62+
menu bar with the Cross Connect Server's name. Clicking on it will
63+
redirect the user to the remote server logged in as the user.
64+
65+
You can change each menu icon (for use with ``web_responsive`` for
66+
instance) by setting the ``Web Icon Data`` in the server configuration.
67+
68+
Bug Tracker
69+
===========
70+
71+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-auth/issues>`_.
72+
In case of trouble, please check there if your issue has already been reported.
73+
If you spotted it first, help us to smash it by providing a detailed and welcomed
74+
`feedback <https://github.com/OCA/server-auth/issues/new?body=module:%20cross_connect_client%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
75+
76+
Do not contact contributors directly about support or help with technical issues.
77+
78+
Credits
79+
=======
80+
81+
Authors
82+
-------
83+
84+
* Akretion
85+
86+
Contributors
87+
------------
88+
89+
- Florian Mounier florian.mounier@akretion.com
90+
91+
Maintainers
92+
-----------
93+
94+
This module is maintained by the OCA.
95+
96+
.. image:: https://odoo-community.org/logo.png
97+
:alt: Odoo Community Association
98+
:target: https://odoo-community.org
99+
100+
OCA, or the Odoo Community Association, is a nonprofit organization whose
101+
mission is to support the collaborative development of Odoo features and
102+
promote its widespread use.
103+
104+
.. |maintainer-paradoxxxzero| image:: https://github.com/paradoxxxzero.png?size=40px
105+
:target: https://github.com/paradoxxxzero
106+
:alt: paradoxxxzero
107+
108+
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
109+
110+
|maintainer-paradoxxxzero|
111+
112+
This module is part of the `OCA/server-auth <https://github.com/OCA/server-auth/tree/16.0/cross_connect_client>`_ project on GitHub.
113+
114+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

cross_connect_client/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import controllers
2+
from . import models
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright 2024 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
{
6+
"name": "Cross Connect Client",
7+
"version": "16.0.1.0.0",
8+
"author": "Akretion, Odoo Community Association (OCA)",
9+
"summary": "Cross Connect Client allows to connect to a "
10+
"Cross Connect Server enabled odoo instance.",
11+
"category": "Tools",
12+
"depends": ["server_environment"],
13+
"website": "https://github.com/OCA/server-auth",
14+
"data": [
15+
"security/ir_model_access.xml",
16+
"views/cross_connect_server_views.xml",
17+
],
18+
"maintainers": ["paradoxxxzero"],
19+
"demo": [],
20+
"installable": True,
21+
"license": "AGPL-3",
22+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import cross_connect
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright 2024 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
from odoo import _
5+
from odoo.exceptions import UserError
6+
from odoo.http import Controller, request, route
7+
8+
9+
class CrossConnectController(Controller):
10+
@route(
11+
["/cross_connect_server/<int:server_id>"],
12+
methods=["GET"],
13+
type="http",
14+
auth="public",
15+
)
16+
def cross_connect(
17+
self,
18+
server_id,
19+
**params,
20+
):
21+
server = request.env["cross.connect.server"].sudo().browse(server_id)
22+
if not server:
23+
raise UserError(_("Server not found"))
24+
25+
url = server._get_cross_connect_url()
26+
return request.redirect(url, local=False)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import cross_connect_server
2+
from . import res_groups
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# Copyright 2024 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
from urllib.parse import urlparse
5+
6+
import requests
7+
8+
from odoo import _, api, fields, models
9+
from odoo.exceptions import UserError
10+
11+
12+
class CrossConnectServer(models.Model):
13+
_name = "cross.connect.server"
14+
_description = "Cross Connect Server"
15+
_inherit = "server.env.mixin"
16+
17+
name = fields.Char(
18+
required=True, compute="_compute_name", readonly=False, store=True
19+
)
20+
server_url = fields.Char(required=True)
21+
api_key = fields.Char(
22+
required=True,
23+
)
24+
group_ids = fields.One2many(
25+
"res.groups",
26+
inverse_name="cross_connect_server_id",
27+
string="Cross Connect Server Groups",
28+
readonly=True,
29+
)
30+
menu_id = fields.Many2one(
31+
"ir.ui.menu",
32+
string="Menu",
33+
help="Menu to display the Cross Connect Server in the menu",
34+
compute="_compute_menu_id",
35+
store=True,
36+
)
37+
web_icon_data = fields.Binary(
38+
compute="_compute_web_icon_data", inverse="_inverse_web_icon_data"
39+
)
40+
41+
@api.depends("server_url")
42+
def _compute_name(self):
43+
for record in self:
44+
if not record.name or record.name == "/":
45+
try:
46+
record.name = urlparse(record.server_url).hostname
47+
except Exception:
48+
record.name = "/"
49+
50+
@api.depends("name", "group_ids")
51+
def _compute_menu_id(self):
52+
for record in self:
53+
if not record.group_ids:
54+
if record.menu_id:
55+
if record.menu_id.action:
56+
record.menu_id.action.unlink()
57+
record.menu_id.unlink()
58+
record.menu_id = False
59+
continue
60+
61+
menu_groups = self.env.ref("base.group_system") | record.group_ids
62+
63+
if not record.menu_id:
64+
action = self.env["ir.actions.act_url"].create(
65+
{
66+
"name": record.name,
67+
"url": f"/cross_connect_server/{record.id}",
68+
"target": "self",
69+
}
70+
)
71+
72+
record.menu_id = self.env["ir.ui.menu"].create(
73+
{
74+
"name": record.name,
75+
"action": f"ir.actions.act_url,{action.id}", # noqa
76+
"web_icon": "cross_connect_client,static/description/web_icon_data.png",
77+
"groups_id": [(6, 0, menu_groups.ids)],
78+
"sequence": 100,
79+
}
80+
)
81+
else:
82+
record.menu_id.name = record.name
83+
record.menu_id.groups_id = [(6, 0, menu_groups.ids)]
84+
85+
@api.depends("menu_id")
86+
def _compute_web_icon_data(self):
87+
for record in self:
88+
record.web_icon_data = record.menu_id.web_icon_data
89+
90+
def _inverse_web_icon_data(self):
91+
for record in self:
92+
record.menu_id.web_icon_data = record.web_icon_data
93+
94+
def _absolute_url_for(self, path):
95+
return f"{self.server_url.rstrip('/')}/cross_connect/{path.lstrip('/')}"
96+
97+
def _request(self, method, url, headers=None, data=None):
98+
headers = headers or {}
99+
headers["api-key"] = self.api_key
100+
response = requests.request(
101+
method,
102+
self._absolute_url_for(url),
103+
headers=headers,
104+
json=data,
105+
timeout=10,
106+
)
107+
response.raise_for_status()
108+
return response.json()
109+
110+
def _get_cross_connect_url(self):
111+
self.ensure_one()
112+
groups = self.env.user.groups_id & self.group_ids
113+
if not groups:
114+
raise UserError(_("You are not allowed to access this server"))
115+
116+
response = self._request(
117+
"POST",
118+
"/access",
119+
data={
120+
"id": self.env.user.id,
121+
"name": self.env.user.name,
122+
"login": self.env.user.login,
123+
"lang": self.env.user.lang,
124+
"groups": [group.cross_connect_server_group_id for group in groups],
125+
},
126+
)
127+
client_id = response.get("client_id")
128+
token = response.get("token")
129+
if not token:
130+
raise UserError(_("Missing token"))
131+
132+
return self._absolute_url_for(f"login/{client_id}/{token}")
133+
134+
def _sync_groups(self):
135+
self.ensure_one()
136+
response = self._request("GET", "/sync")
137+
remote_groups = response.get("groups", [])
138+
# Removing groups that are not on the remote server
139+
remote_groups_ids = {remote_group["id"] for remote_group in remote_groups}
140+
self.group_ids.filtered(
141+
lambda group: group.cross_connect_server_group_id not in remote_groups_ids
142+
).unlink()
143+
144+
# Create or Update existing groups
145+
for remote_group in remote_groups:
146+
existing_group = self.group_ids.filtered(
147+
lambda group: group.cross_connect_server_group_id == remote_group["id"]
148+
)
149+
if existing_group:
150+
existing_group.sudo().write(
151+
{
152+
"name": f"{self.name}: {remote_group['name']}",
153+
"comment": remote_group["comment"],
154+
}
155+
)
156+
else:
157+
self.env["res.groups"].sudo().create(
158+
{
159+
"cross_connect_server_id": self.id,
160+
"cross_connect_server_group_id": remote_group["id"],
161+
"name": f"{self.name}: {remote_group['name']}",
162+
"comment": remote_group["comment"],
163+
}
164+
)
165+
166+
def action_sync(self):
167+
for record in self:
168+
record._sync_groups()
169+
170+
def action_disable(self):
171+
for record in self:
172+
record.group_ids.unlink()
173+
174+
@property
175+
def _server_env_fields(self):
176+
return {"api_key": {}}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright 2024 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
from odoo import fields, models
6+
7+
8+
class ResGroups(models.Model):
9+
_inherit = "res.groups"
10+
11+
cross_connect_server_id = fields.Many2one(
12+
"cross.connect.server", string="Originating Cross Connect Server"
13+
)
14+
cross_connect_server_group_id = fields.Integer(
15+
string="Originating Cross Connect Server Group ID"
16+
)
17+
18+
_sql_constraints = [
19+
(
20+
"cross_connect_server_group_id_cross_connect_server_id_unique",
21+
"unique (cross_connect_server_group_id, cross_connect_server_id)",
22+
"Cross Connect Server Group ID must be unique per Cross Connect Server",
23+
)
24+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Florian Mounier <florian.mounier@akretion.com>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
This module allows this odoo instance users to connect directly on another odoo instance
2+
where the module `cross_connect_server` is installed.

0 commit comments

Comments
 (0)