bemade-addons/odoo_unifi_manager/models/unifi_firewall_rule_template.py
2025-01-06 08:02:03 -05:00

157 lines
4.6 KiB
Python

from odoo import models, fields, api
from odoo.exceptions import ValidationError
import re
import ipaddress
class FirewallRuleTemplate(models.Model):
_name = 'unifi.firewall.rule.template'
_description = 'Firewall Rule Template'
_order = 'rule_index'
name = fields.Char(
string='Template Name',
required=True
)
ctrl_id = fields.Many2one(
'unifi.ctrl',
string='Controller',
required=True,
help="Controller where this rule is applied."
)
enabled = fields.Boolean(
string='Enabled',
default=True,
help="Whether this rule is active"
)
ruleset = fields.Selection([
('LAN_IN', 'LAN In'),
('LAN_OUT', 'LAN Out'),
('LAN_LOCAL', 'LAN Local'),
('WAN_IN', 'WAN In'),
('WAN_OUT', 'WAN Out'),
('WAN_LOCAL', 'WAN Local')
], string='Rule Set', required=True, default='LAN_IN')
rule_index = fields.Integer(
string='Rule Index',
help="Order in which the rule is applied",
default=2000
)
action = fields.Selection(
selection=[
('accept', 'Accept'),
('drop', 'Drop'),
('reject', 'Reject')
],
string='Action',
required=True
)
src_ip = fields.Char(
string='Source IP',
help="Source IP address or network (e.g., 192.168.1.0/24)"
)
dst_ip = fields.Char(
string='Destination IP',
help="Destination IP address or network (e.g., 192.168.1.0/24)"
)
protocol = fields.Selection(
selection=[
('all', 'All'),
('tcp', 'TCP'),
('udp', 'UDP'),
('icmp', 'ICMP')
],
string='Protocol',
required=True,
default='all'
)
port = fields.Char(
string='Port',
help="Port or port range (e.g., 80 or 8000-9000)"
)
firewall_group_id = fields.Many2one(
'unifi.firewall.group',
string='Firewall Group',
help="Associated firewall group"
)
categories = fields.Many2many(
'unifi.dpi.category',
string='DPI Categories',
help="Deep Packet Inspection categories this rule applies to"
)
@api.constrains('port')
def _check_port(self):
"""Validate port format."""
for record in self:
if not record.port:
continue
# Check single port
if record.port.isdigit():
port = int(record.port)
if not (1 <= port <= 65535):
raise ValidationError("Port must be between 1 and 65535")
continue
# Check port range
if '-' in record.port:
try:
start, end = map(int, record.port.split('-'))
if not (1 <= start <= end <= 65535):
raise ValidationError("Port range must be between 1 and 65535")
except ValueError:
raise ValidationError("Invalid port range format. Use format: start-end")
else:
raise ValidationError("Invalid port format. Use a single port or range (e.g., 80 or 8000-9000)")
@api.constrains('src_ip', 'dst_ip')
def _check_ip_addresses(self):
"""Validate IP address formats."""
for record in self:
for field in [record.src_ip, record.dst_ip]:
if not field:
continue
try:
# Handle CIDR notation
if '/' in field:
ipaddress.ip_network(field)
else:
ipaddress.ip_address(field)
except ValueError:
raise ValidationError(f"Invalid IP address format: {field}")
def copy_to_controller(self):
"""Copy this template to the actual firewall rules on the controller."""
self.ensure_one()
client = self.ctrl_id.get_client()
rule_data = {
'name': self.name,
'enabled': self.enabled,
'ruleset': self.ruleset,
'rule_index': self.rule_index,
'protocol': self.protocol,
'action': self.action,
}
if self.port:
rule_data['port'] = self.port
if self.src_ip:
rule_data['src'] = self.src_ip
if self.dst_ip:
rule_data['dst'] = self.dst_ip
if self.categories:
rule_data['categories'] = self.categories.mapped('category_id')
return client.create_firewall_rule(rule_data)