Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
577eee9
[ADD] cetmix_tower_jet_isolation: base module
marionumza Apr 17, 2026
bb589d6
[FIX] cetmix_tower_jet_isolation: resolve pre-commit violations
marionumza Apr 17, 2026
c0066cd
[FIX] cetmix_tower_jet_isolation: update author metadata
marionumza Apr 17, 2026
834ea5d
[FIX] cetmix_tower_jet_isolation: enforce strict pylint rules
marionumza Apr 17, 2026
6582561
[FIX] cetmix_tower_jet_isolation: add README.rst, fix ruff formatting…
marionumza Apr 17, 2026
2fe26fc
[FIX] cetmix_tower_jet_isolation: fix line length to comply with ruff…
marionumza Apr 17, 2026
1028b27
feat(jet_isolation): hide tag selector for non‑manager users, bump ve…
marionumza Apr 17, 2026
7c1970e
[ADD] cetmix_tower_jet_isolation: add uninstall hook to reset isolati…
marionumza Apr 18, 2026
1126d98
[FIX] cetmix_tower_jet_isolation: expose uninstall_hook at module roo…
marionumza Apr 18, 2026
6c96323
[IMP] cetmix_tower_jet_isolation: show forced tags as read-only in is…
marionumza Apr 18, 2026
385a528
[IMP] cetmix_tower_jet_isolation: show forced tags as truly readonly …
marionumza Apr 18, 2026
f74940a
[FIX] cetmix_tower_jet_isolation: rename fields label to avoid collision
marionumza Apr 18, 2026
532c216
refactor: align cx_tower_jet_template.py with repo style and resolve …
marionumza Apr 22, 2026
f3a5b1f
feat: enforce forced_applicability and use dynamic selection to avoid…
marionumza Apr 22, 2026
90f8a08
refactor: improve Many2many definitions with explicit columns and bet…
marionumza Apr 22, 2026
acf6c98
fix: clear forced values when isolation mode is disabled
marionumza Apr 22, 2026
f5f3d24
refactor: simplify wizard compute logic and improve dependency tracking
marionumza Apr 22, 2026
2e0d5ad
fix: ensure isolation enforcement in wizards by inspecting all select…
marionumza Apr 22, 2026
665e6fa
style: align wizard class names and imports with repository standards
marionumza Apr 22, 2026
d4c3eed
security: enforce isolation constraints on server side in wizards
marionumza Apr 22, 2026
3ab9757
security: prevent privilege escalation in wizards regarding applicabi…
marionumza Apr 22, 2026
baf0b16
ui: combine readonly conditions for applicability in wizards
marionumza Apr 22, 2026
d152c97
ui: remove unnecessary group restriction on tag_ids in command wizard
marionumza Apr 22, 2026
0b7ae3e
fix: ensure uninstall hook clears all templates including archived ones
marionumza Apr 22, 2026
fe0c165
style: fix pre-commit issues (line length and redundant strings)
marionumza Apr 22, 2026
d3e4c2c
docs: add readme fragments and generate README.rst
marionumza Apr 22, 2026
5238f08
ui: format XML attributes for prettier compatibility
marionumza Apr 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions cetmix_tower_jet_isolation/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
==========================
Cetmix Tower Jet Isolation
==========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-cetmix%2Fcetmix--tower-lightgray.png?logo=github
:target: https://github.com/cetmix/cetmix-tower
:alt: cetmix/cetmix-tower

|badge1| |badge2| |badge3|

Adds strict isolation mode for Jets, preventing users from altering command visibility.

**Table of contents**

.. contents::
:local:

Installation
============

To install this module, you need to:

1. Clone cetmix-tower repository.
2. Install this module `cetmix_tower_jet_isolation` into your Odoo instance.

Configuration
=============

To configure this module, you need to:

1. Go to Tower > Jet Templates.
2. Open a Jet Template.
3. Enable 'Isolation Mode' and configure forced applicability and tags.

Usage
=====

When users run a command or a flight plan from an isolated Jet, the wizard will have the applicability options and tag selections locked, enforcing the parameters defined by the administrator in the Template.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/cetmix/cetmix-tower/issues>`_.

Credits
=======

Authors
~~~~~~~

* Cetmix
* Crumges

Maintainers
~~~~~~~~~~~

This module is maintained by the Cetmix team.
2 changes: 2 additions & 0 deletions cetmix_tower_jet_isolation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizards
18 changes: 18 additions & 0 deletions cetmix_tower_jet_isolation/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Cetmix Tower Jet Isolation",
"version": "18.0.1.0.1",
"category": "Tower",
"summary": "Adds strict isolation mode for Jets.",
"author": "Cetmix, Crumges",
"website": "https://tower.cetmix.com",
"depends": [
"cetmix_tower_server",
],
"data": [
"views/cx_tower_jet_template_views.xml",
"views/cx_tower_command_run_wizard_views.xml",
"views/cx_tower_plan_run_wizard_views.xml",
],
"installable": True,
"license": "AGPL-3",
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
1 change: 1 addition & 0 deletions cetmix_tower_jet_isolation/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import cx_tower_jet_template
22 changes: 22 additions & 0 deletions cetmix_tower_jet_isolation/models/cx_tower_jet_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from odoo import fields, models


class CxTowerJetTemplate(models.Model):
_inherit = "cx.tower.jet.template"

isolation_mode = fields.Boolean(
help="Prevents changing applicability or tags when running commands."
)
forced_applicability = fields.Selection(
[("this", "For selected server(s)"), ("shared", "Non server restricted")]
)

forced_command_tag_ids = fields.Many2many(
comodel_name="cx.tower.tag",
relation="cx_tower_template_forced_command_tag_rel",
)

forced_plan_tag_ids = fields.Many2many(
comodel_name="cx.tower.tag",
relation="cx_tower_template_forced_plan_tag_rel",
)
Comment thread
marionumza marked this conversation as resolved.
3 changes: 3 additions & 0 deletions cetmix_tower_jet_isolation/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="cx_tower_command_run_wizard_view_form_isolation" model="ir.ui.view">
<field name="name">cx.tower.command.run.wizard.view.form.isolation</field>
<field name="model">cx.tower.command.run.wizard</field>
<field
name="inherit_id"
ref="cetmix_tower_server.cx_tower_command_run_wizard_view_form"
/>
<field name="arch" type="xml">
<xpath expr="//field[@name='tag_ids']" position="before">
<field name="is_restricted_context" invisible="1" />
</xpath>

<xpath expr="//label[@for='applicability']" position="attributes">
<attribute name="invisible">is_restricted_context or result</attribute>
</xpath>

<xpath expr="//field[@name='applicability']" position="attributes">
<attribute name="readonly">is_restricted_context</attribute>
<attribute name="force_save">1</attribute>
</xpath>
Comment thread
marionumza marked this conversation as resolved.

<xpath expr="//field[@name='tag_ids']" position="attributes">
<attribute name="groups">cetmix_tower_server.group_manager</attribute>
<attribute name="invisible">is_restricted_context or result</attribute>
<attribute name="force_save">1</attribute>
</xpath>
Comment thread
marionumza marked this conversation as resolved.

<xpath expr="//notebook" position="attributes">
<attribute name="groups" />
</xpath>
<xpath expr="//page[@name='code']" position="attributes">
<attribute name="invisible">is_restricted_context</attribute>
</xpath>
<xpath expr="//field[@name='rendered_code']" position="attributes">
<attribute name="groups" />
<attribute name="readonly">1</attribute>
</xpath>
Comment thread
marionumza marked this conversation as resolved.
<xpath
expr="//field[@name='custom_variable_value_ids']"
position="attributes"
>
<attribute
name="readonly"
>not have_access_to_server or is_restricted_context</attribute>
</xpath>
</field>
</record>
</odoo>
33 changes: 33 additions & 0 deletions cetmix_tower_jet_isolation/views/cx_tower_jet_template_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="cx_tower_jet_template_view_form_isolation" model="ir.ui.view">
<field name="name">cx.tower.jet.template.view.form.isolation</field>
<field name="model">cx.tower.jet.template</field>
<field
name="inherit_id"
ref="cetmix_tower_server.cx_tower_jet_template_view_form"
/>
<field name="arch" type="xml">
<xpath expr="//group[@name='general']" position="after">
<group name="isolation" string="Execution Security (Isolation Mode)">
<field name="isolation_mode" widget="boolean_toggle" />
<field
name="forced_applicability"
invisible="not isolation_mode"
required="isolation_mode"
/>
<field
name="forced_command_tag_ids"
widget="many2many_tags"
invisible="not isolation_mode"
/>
<field
name="forced_plan_tag_ids"
widget="many2many_tags"
invisible="not isolation_mode"
/>
</group>
</xpath>
</field>
</record>
</odoo>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="cx_tower_plan_run_wizard_view_form_isolation" model="ir.ui.view">
<field name="name">cx.tower.plan.run.wizard.view.form.isolation</field>
<field name="model">cx.tower.plan.run.wizard</field>
<field
name="inherit_id"
ref="cetmix_tower_server.cx_tower_plan_run_wizard_view_form"
/>
<field name="arch" type="xml">
<xpath expr="//field[@name='tag_ids']" position="before">
<field name="is_restricted_context" invisible="1" />
</xpath>

<xpath expr="//label[@for='applicability']" position="attributes">
<attribute name="invisible">is_restricted_context</attribute>
</xpath>

<xpath expr="//field[@name='applicability']" position="attributes">
<attribute name="readonly">is_restricted_context</attribute>
<attribute name="force_save">1</attribute>
</xpath>
Comment thread
marionumza marked this conversation as resolved.

<xpath expr="//field[@name='tag_ids']" position="attributes">
<attribute name="invisible">is_restricted_context</attribute>
<attribute name="force_save">1</attribute>
</xpath>

<xpath
expr="//field[@name='custom_variable_value_ids']"
position="attributes"
>
<attribute name="readonly">is_restricted_context</attribute>
</xpath>
</field>
</record>
</odoo>
2 changes: 2 additions & 0 deletions cetmix_tower_jet_isolation/wizards/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import cx_tower_command_run_wizard
from . import cx_tower_plan_run_wizard
45 changes: 45 additions & 0 deletions cetmix_tower_jet_isolation/wizards/cx_tower_command_run_wizard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from odoo import api, fields, models


class CxTowerCommandRunWizardFilter(models.TransientModel):
_inherit = "cx.tower.command.run.wizard"

is_restricted_context = fields.Boolean(compute="_compute_is_restricted_context")

@api.depends("jet_ids")
def _compute_is_restricted_context(self):
is_global_manager = self.env.user.has_group("cetmix_tower_server.group_manager")
for record in self:
jets = record.jet_ids or self.env["cx.tower.jet"].browse(
self.env.context.get("default_jet_ids", [])
)

is_isolated = bool(
jets and any(j.jet_template_id.isolation_mode for j in jets)
)

if is_global_manager:
is_manager = True
elif jets and all(self.env.user in j.manager_ids for j in jets):
is_manager = True
else:
is_manager = False

record.is_restricted_context = is_isolated and not is_manager

@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
if "default_jet_ids" in self.env.context:
jet_ids = self.env["cx.tower.jet"].browse(
self.env.context["default_jet_ids"]
)
if jet_ids:
template = jet_ids[0].jet_template_id

if template.isolation_mode:
if template.forced_applicability:
res["applicability"] = template.forced_applicability
if template.forced_command_tag_ids:
res["tag_ids"] = [(6, 0, template.forced_command_tag_ids.ids)]
return res
Comment thread
marionumza marked this conversation as resolved.
45 changes: 45 additions & 0 deletions cetmix_tower_jet_isolation/wizards/cx_tower_plan_run_wizard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from odoo import api, fields, models


class CxTowerPlanRunWizardFilter(models.TransientModel):
_inherit = "cx.tower.plan.run.wizard"

is_restricted_context = fields.Boolean(compute="_compute_is_restricted_context")

@api.depends("jet_ids")
def _compute_is_restricted_context(self):
is_global_manager = self.env.user.has_group("cetmix_tower_server.group_manager")
for record in self:
jets = record.jet_ids or self.env["cx.tower.jet"].browse(
self.env.context.get("default_jet_ids", [])
)

is_isolated = bool(
jets and any(j.jet_template_id.isolation_mode for j in jets)
)

if is_global_manager:
is_manager = True
elif jets and all(self.env.user in j.manager_ids for j in jets):
is_manager = True
else:
is_manager = False

record.is_restricted_context = is_isolated and not is_manager

@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
if "default_jet_ids" in self.env.context:
jet_ids = self.env["cx.tower.jet"].browse(
self.env.context["default_jet_ids"]
)
if jet_ids:
template = jet_ids[0].jet_template_id

if template.isolation_mode:
if template.forced_applicability:
res["applicability"] = template.forced_applicability
if template.forced_plan_tag_ids:
res["tag_ids"] = [(6, 0, template.forced_plan_tag_ids.ids)]
return res
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Loading