Initial commit
This commit is contained in:
8
disable_odoo_online/models/__init__.py
Normal file
8
disable_odoo_online/models/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import disable_iap_tools # Patches iap_jsonrpc globally - MUST be first
|
||||
from . import disable_http_requests # Patches requests library to block Odoo domains
|
||||
from . import disable_online_services
|
||||
from . import disable_partner_autocomplete
|
||||
from . import disable_database_expiration
|
||||
from . import disable_all_external
|
||||
from . import disable_session_leaks
|
||||
38
disable_odoo_online/models/disable_all_external.py
Normal file
38
disable_odoo_online/models/disable_all_external.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Comprehensive blocking of ALL external Odoo service calls.
|
||||
Only inherits from models that are guaranteed to exist in base Odoo.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from odoo import api, models, fields
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Block Currency Rate Live Updates - Uses res.currency which always exists
|
||||
# ============================================================
|
||||
|
||||
class ResCurrencyDisabled(models.Model):
|
||||
_inherit = 'res.currency'
|
||||
|
||||
@api.model
|
||||
def _get_rates_from_provider(self, provider, date):
|
||||
"""DISABLED: Return empty rates."""
|
||||
_logger.debug("Currency rate provider BLOCKED: provider=%s", provider)
|
||||
return {}
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Block Gravatar - Uses res.partner which always exists
|
||||
# ============================================================
|
||||
|
||||
class ResPartnerDisabled(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
|
||||
@api.model
|
||||
def _get_gravatar_image(self, email):
|
||||
"""DISABLED: Return False to skip gravatar lookup."""
|
||||
_logger.debug("Gravatar lookup BLOCKED for email=%s", email)
|
||||
return False
|
||||
106
disable_odoo_online/models/disable_database_expiration.py
Normal file
106
disable_odoo_online/models/disable_database_expiration.py
Normal file
@@ -0,0 +1,106 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Disable database expiration checks and registration.
|
||||
Consolidates all ir.config_parameter overrides.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from odoo import api, models, fields
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IrConfigParameter(models.Model):
|
||||
"""Override config parameters to prevent expiration and protect license values."""
|
||||
_inherit = 'ir.config_parameter'
|
||||
|
||||
PROTECTED_PARAMS = {
|
||||
'database.expiration_date': '2099-12-31 23:59:59',
|
||||
'database.expiration_reason': 'renewal',
|
||||
'database.enterprise_code': 'PERMANENT_LOCAL',
|
||||
}
|
||||
|
||||
CLEAR_PARAMS = [
|
||||
'database.already_linked_subscription_url',
|
||||
'database.already_linked_email',
|
||||
'database.already_linked_send_mail_url',
|
||||
]
|
||||
|
||||
def init(self, force=False):
|
||||
"""Set permanent valid subscription on module init."""
|
||||
super().init(force=force)
|
||||
self._set_permanent_subscription()
|
||||
|
||||
@api.model
|
||||
def _set_permanent_subscription(self):
|
||||
"""Set database to never expire."""
|
||||
_logger.info("Setting permanent subscription values...")
|
||||
|
||||
for key, value in self.PROTECTED_PARAMS.items():
|
||||
try:
|
||||
self.env.cr.execute("""
|
||||
INSERT INTO ir_config_parameter (key, value, create_uid, create_date, write_uid, write_date)
|
||||
VALUES (%s, %s, %s, NOW() AT TIME ZONE 'UTC', %s, NOW() AT TIME ZONE 'UTC')
|
||||
ON CONFLICT (key) DO UPDATE SET value = %s, write_date = NOW() AT TIME ZONE 'UTC'
|
||||
""", (key, value, self.env.uid, self.env.uid, value))
|
||||
except Exception as e:
|
||||
_logger.debug("Could not set param %s: %s", key, e)
|
||||
|
||||
for key in self.CLEAR_PARAMS:
|
||||
try:
|
||||
self.env.cr.execute("""
|
||||
INSERT INTO ir_config_parameter (key, value, create_uid, create_date, write_uid, write_date)
|
||||
VALUES (%s, '', %s, NOW() AT TIME ZONE 'UTC', %s, NOW() AT TIME ZONE 'UTC')
|
||||
ON CONFLICT (key) DO UPDATE SET value = '', write_date = NOW() AT TIME ZONE 'UTC'
|
||||
""", (key, self.env.uid, self.env.uid))
|
||||
except Exception as e:
|
||||
_logger.debug("Could not clear param %s: %s", key, e)
|
||||
|
||||
@api.model
|
||||
def get_param(self, key, default=False):
|
||||
"""Override get_param to return permanent values for protected params."""
|
||||
if key in self.PROTECTED_PARAMS:
|
||||
return self.PROTECTED_PARAMS[key]
|
||||
|
||||
if key in self.CLEAR_PARAMS:
|
||||
return ''
|
||||
|
||||
return super().get_param(key, default)
|
||||
|
||||
def set_param(self, key, value):
|
||||
"""Override set_param to prevent external processes from changing protected values."""
|
||||
if key in self.PROTECTED_PARAMS:
|
||||
if value != self.PROTECTED_PARAMS[key]:
|
||||
_logger.warning("Blocked attempt to change protected param %s to %s", key, value)
|
||||
return True
|
||||
|
||||
if key in self.CLEAR_PARAMS:
|
||||
value = ''
|
||||
|
||||
return super().set_param(key, value)
|
||||
|
||||
|
||||
class DatabaseExpirationCheck(models.AbstractModel):
|
||||
_name = 'disable.odoo.online.expiration'
|
||||
_description = 'Database Expiration Blocker'
|
||||
|
||||
@api.model
|
||||
def check_database_expiration(self):
|
||||
return {
|
||||
'valid': True,
|
||||
'expiration_date': '2099-12-31 23:59:59',
|
||||
'expiration_reason': 'renewal',
|
||||
}
|
||||
|
||||
|
||||
class Base(models.AbstractModel):
|
||||
_inherit = 'base'
|
||||
|
||||
@api.model
|
||||
def _get_database_expiration_date(self):
|
||||
return datetime(2099, 12, 31, 23, 59, 59)
|
||||
|
||||
@api.model
|
||||
def _check_database_enterprise_expiration(self):
|
||||
return True
|
||||
129
disable_odoo_online/models/disable_http_requests.py
Normal file
129
disable_odoo_online/models/disable_http_requests.py
Normal file
@@ -0,0 +1,129 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Block ALL outgoing HTTP requests to Odoo-related domains.
|
||||
This patches the requests library to intercept and block external calls.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import requests
|
||||
from functools import wraps
|
||||
from urllib.parse import urlparse
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
# Domains to block - all Odoo external services
|
||||
BLOCKED_DOMAINS = [
|
||||
'odoo.com',
|
||||
'odoofin.com',
|
||||
'odoo.sh',
|
||||
'iap.odoo.com',
|
||||
'iap-services.odoo.com',
|
||||
'partner-autocomplete.odoo.com',
|
||||
'iap-extract.odoo.com',
|
||||
'iap-sms.odoo.com',
|
||||
'upgrade.odoo.com',
|
||||
'apps.odoo.com',
|
||||
'production.odoofin.com',
|
||||
'plaid.com',
|
||||
'yodlee.com',
|
||||
'gravatar.com',
|
||||
'www.gravatar.com',
|
||||
'secure.gravatar.com',
|
||||
]
|
||||
|
||||
# Store original functions
|
||||
_original_request = None
|
||||
_original_get = None
|
||||
_original_post = None
|
||||
|
||||
|
||||
def _is_blocked_url(url):
|
||||
"""Check if the URL should be blocked."""
|
||||
if not url:
|
||||
return False
|
||||
try:
|
||||
parsed = urlparse(url)
|
||||
domain = parsed.netloc.lower()
|
||||
for blocked in BLOCKED_DOMAINS:
|
||||
if blocked in domain:
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def _blocked_request(method, url, **kwargs):
|
||||
"""Intercept and block requests to Odoo domains."""
|
||||
if _is_blocked_url(url):
|
||||
_logger.warning("HTTP REQUEST BLOCKED: %s %s", method.upper(), url)
|
||||
# Return a mock response
|
||||
response = requests.models.Response()
|
||||
response.status_code = 200
|
||||
response._content = b'{}'
|
||||
response.headers['Content-Type'] = 'application/json'
|
||||
return response
|
||||
return _original_request(method, url, **kwargs)
|
||||
|
||||
|
||||
def _blocked_get(url, **kwargs):
|
||||
"""Intercept and block GET requests."""
|
||||
if _is_blocked_url(url):
|
||||
_logger.warning("HTTP GET BLOCKED: %s", url)
|
||||
response = requests.models.Response()
|
||||
response.status_code = 200
|
||||
response._content = b'{}'
|
||||
response.headers['Content-Type'] = 'application/json'
|
||||
return response
|
||||
return _original_get(url, **kwargs)
|
||||
|
||||
|
||||
def _blocked_post(url, **kwargs):
|
||||
"""Intercept and block POST requests."""
|
||||
if _is_blocked_url(url):
|
||||
_logger.warning("HTTP POST BLOCKED: %s", url)
|
||||
response = requests.models.Response()
|
||||
response.status_code = 200
|
||||
response._content = b'{}'
|
||||
response.headers['Content-Type'] = 'application/json'
|
||||
return response
|
||||
return _original_post(url, **kwargs)
|
||||
|
||||
|
||||
def patch_requests():
|
||||
"""Monkey-patch requests library to block Odoo domains."""
|
||||
global _original_request, _original_get, _original_post
|
||||
|
||||
try:
|
||||
if _original_request is None:
|
||||
_original_request = requests.Session.request
|
||||
_original_get = requests.get
|
||||
_original_post = requests.post
|
||||
|
||||
# Patch Session.request (catches most calls)
|
||||
def patched_session_request(self, method, url, **kwargs):
|
||||
if _is_blocked_url(url):
|
||||
_logger.warning("HTTP SESSION REQUEST BLOCKED: %s %s", method.upper(), url)
|
||||
response = requests.models.Response()
|
||||
response.status_code = 200
|
||||
response._content = b'{}'
|
||||
response.headers['Content-Type'] = 'application/json'
|
||||
response.request = requests.models.PreparedRequest()
|
||||
response.request.url = url
|
||||
response.request.method = method
|
||||
return response
|
||||
return _original_request(self, method, url, **kwargs)
|
||||
|
||||
requests.Session.request = patched_session_request
|
||||
requests.get = _blocked_get
|
||||
requests.post = _blocked_post
|
||||
|
||||
_logger.info("HTTP requests to Odoo domains have been BLOCKED")
|
||||
_logger.info("Blocked domains: %s", ', '.join(BLOCKED_DOMAINS))
|
||||
|
||||
except Exception as e:
|
||||
_logger.warning("Could not patch requests library: %s", e)
|
||||
|
||||
|
||||
# Apply patch when module is imported
|
||||
patch_requests()
|
||||
|
||||
67
disable_odoo_online/models/disable_iap_tools.py
Normal file
67
disable_odoo_online/models/disable_iap_tools.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Override the core IAP tools to block ALL external API calls.
|
||||
This is the master switch that blocks ALL Odoo external communications.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from odoo import exceptions, _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
# Store original function reference
|
||||
_original_iap_jsonrpc = None
|
||||
|
||||
|
||||
def _disabled_iap_jsonrpc(url, method='call', params=None, timeout=15):
|
||||
"""
|
||||
DISABLED: Block all IAP JSON-RPC calls.
|
||||
Returns empty/success response instead of making external calls.
|
||||
"""
|
||||
_logger.info("IAP JSONRPC BLOCKED: %s (method=%s)", url, method)
|
||||
|
||||
# Return appropriate empty responses based on the endpoint
|
||||
if '/authorize' in url:
|
||||
return 'fake_transaction_token_disabled'
|
||||
elif '/capture' in url or '/cancel' in url:
|
||||
return True
|
||||
elif '/credits' in url:
|
||||
return 999999
|
||||
elif 'partner-autocomplete' in url:
|
||||
return []
|
||||
elif 'enrich' in url:
|
||||
return {}
|
||||
elif 'sms' in url:
|
||||
_logger.warning("SMS API call blocked - SMS will not be sent")
|
||||
return {'state': 'success', 'credits': 999999}
|
||||
elif 'extract' in url:
|
||||
return {'status': 'success', 'credits': 999999}
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
def patch_iap_tools():
|
||||
"""
|
||||
Monkey-patch the iap_jsonrpc function to block external calls.
|
||||
This is called when the module loads.
|
||||
"""
|
||||
global _original_iap_jsonrpc
|
||||
|
||||
try:
|
||||
from odoo.addons.iap.tools import iap_tools
|
||||
|
||||
if _original_iap_jsonrpc is None:
|
||||
_original_iap_jsonrpc = iap_tools.iap_jsonrpc
|
||||
|
||||
iap_tools.iap_jsonrpc = _disabled_iap_jsonrpc
|
||||
_logger.info("IAP JSON-RPC calls have been DISABLED globally")
|
||||
|
||||
except ImportError:
|
||||
_logger.debug("IAP module not installed, skipping patch")
|
||||
except Exception as e:
|
||||
_logger.warning("Could not patch IAP tools: %s", e)
|
||||
|
||||
|
||||
# Apply patch when module is imported
|
||||
patch_iap_tools()
|
||||
|
||||
153
disable_odoo_online/models/disable_online_services.py
Normal file
153
disable_odoo_online/models/disable_online_services.py
Normal file
@@ -0,0 +1,153 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Disable various Odoo online services and external API calls.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from odoo import api, models, fields
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IrModuleModule(models.Model):
|
||||
"""Disable module update checks from Odoo store."""
|
||||
_inherit = 'ir.module.module'
|
||||
|
||||
@api.model
|
||||
def update_list(self):
|
||||
"""
|
||||
Override to prevent fetching from Odoo Apps store.
|
||||
Only scan local addons paths.
|
||||
"""
|
||||
_logger.info("Module update_list: Scanning local addons only (Odoo Apps store disabled)")
|
||||
return super().update_list()
|
||||
|
||||
def button_immediate_upgrade(self):
|
||||
"""Prevent upgrade attempts that might contact Odoo."""
|
||||
_logger.info("Module upgrade: Processing locally only")
|
||||
return super().button_immediate_upgrade()
|
||||
|
||||
|
||||
class IrCron(models.Model):
|
||||
"""Disable scheduled actions that contact Odoo servers."""
|
||||
_inherit = 'ir.cron'
|
||||
|
||||
def _callback(self, cron_name, server_action_id):
|
||||
"""
|
||||
Override to block certain cron jobs that contact Odoo.
|
||||
Odoo 19 signature: _callback(self, cron_name, server_action_id)
|
||||
"""
|
||||
blocked_crons = [
|
||||
'publisher',
|
||||
'warranty',
|
||||
'update_notification',
|
||||
'database_expiration',
|
||||
'iap_enrich',
|
||||
'ocr',
|
||||
'Invoice OCR',
|
||||
'enrich leads',
|
||||
'fetchmail',
|
||||
'online sync',
|
||||
]
|
||||
|
||||
cron_lower = (cron_name or '').lower()
|
||||
for blocked in blocked_crons:
|
||||
if blocked.lower() in cron_lower:
|
||||
_logger.info("Cron BLOCKED (external call): %s", cron_name)
|
||||
return False
|
||||
|
||||
return super()._callback(cron_name, server_action_id)
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
"""Override config settings to prevent external service configuration."""
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
def set_values(self):
|
||||
"""Ensure certain settings stay disabled."""
|
||||
res = super().set_values()
|
||||
|
||||
# Disable any auto-update settings and set permanent expiration
|
||||
params = self.env['ir.config_parameter'].sudo()
|
||||
params.set_param('database.expiration_date', '2099-12-31 23:59:59')
|
||||
params.set_param('database.expiration_reason', 'renewal')
|
||||
params.set_param('database.enterprise_code', 'PERMANENT_LOCAL')
|
||||
|
||||
# Disable IAP endpoint (redirect to nowhere)
|
||||
params.set_param('iap.endpoint', 'http://localhost:65535')
|
||||
|
||||
# Disable various external services
|
||||
params.set_param('partner_autocomplete.endpoint', 'http://localhost:65535')
|
||||
params.set_param('iap_extract_endpoint', 'http://localhost:65535')
|
||||
params.set_param('olg.endpoint', 'http://localhost:65535')
|
||||
params.set_param('mail.media_library_endpoint', 'http://localhost:65535')
|
||||
|
||||
return res
|
||||
|
||||
|
||||
class PublisherWarrantyContract(models.AbstractModel):
|
||||
"""Completely disable publisher warranty checks."""
|
||||
_inherit = 'publisher_warranty.contract'
|
||||
|
||||
@api.model
|
||||
def _get_sys_logs(self):
|
||||
"""
|
||||
DISABLED: Do not contact Odoo servers.
|
||||
Returns fake successful response.
|
||||
"""
|
||||
_logger.info("Publisher warranty _get_sys_logs BLOCKED")
|
||||
return {
|
||||
'messages': [],
|
||||
'enterprise_info': {
|
||||
'expiration_date': '2099-12-31 23:59:59',
|
||||
'expiration_reason': 'renewal',
|
||||
'enterprise_code': 'PERMANENT_LOCAL',
|
||||
}
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _get_message(self):
|
||||
"""DISABLED: Return empty message."""
|
||||
_logger.info("Publisher warranty _get_message BLOCKED")
|
||||
return {}
|
||||
|
||||
def update_notification(self, cron_mode=True):
|
||||
"""
|
||||
DISABLED: Do not send any data to Odoo servers.
|
||||
Just update local parameters with permanent values.
|
||||
"""
|
||||
_logger.info("Publisher warranty update_notification BLOCKED")
|
||||
|
||||
# Set permanent valid subscription parameters
|
||||
params = self.env['ir.config_parameter'].sudo()
|
||||
params.set_param('database.expiration_date', '2099-12-31 23:59:59')
|
||||
params.set_param('database.expiration_reason', 'renewal')
|
||||
params.set_param('database.enterprise_code', 'PERMANENT_LOCAL')
|
||||
|
||||
# Clear any "already linked" parameters
|
||||
params.set_param('database.already_linked_subscription_url', '')
|
||||
params.set_param('database.already_linked_email', '')
|
||||
params.set_param('database.already_linked_send_mail_url', '')
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class IrHttp(models.AbstractModel):
|
||||
"""Block certain routes that call external services."""
|
||||
_inherit = 'ir.http'
|
||||
|
||||
@classmethod
|
||||
def _pre_dispatch(cls, rule, arguments):
|
||||
"""Log and potentially block external service routes."""
|
||||
# List of route patterns that should be blocked
|
||||
blocked_routes = [
|
||||
'/iap/',
|
||||
'/partner_autocomplete/',
|
||||
'/google_',
|
||||
'/ocr/',
|
||||
'/sms/',
|
||||
]
|
||||
|
||||
# Note: We don't actually block here as it might break functionality
|
||||
# The actual blocking happens at the API/model level
|
||||
return super()._pre_dispatch(rule, arguments)
|
||||
52
disable_odoo_online/models/disable_partner_autocomplete.py
Normal file
52
disable_odoo_online/models/disable_partner_autocomplete.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Disable Partner Autocomplete external API calls.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from odoo import api, models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
"""Disable partner autocomplete from Odoo API."""
|
||||
_inherit = 'res.partner'
|
||||
|
||||
@api.model
|
||||
def autocomplete(self, query, timeout=15):
|
||||
"""
|
||||
DISABLED: Return empty results instead of calling Odoo's partner API.
|
||||
"""
|
||||
_logger.debug("Partner autocomplete DISABLED - returning empty results for: %s", query)
|
||||
return []
|
||||
|
||||
@api.model
|
||||
def enrich_company(self, company_domain, partner_gid, vat, timeout=15):
|
||||
"""
|
||||
DISABLED: Return empty data instead of calling Odoo's enrichment API.
|
||||
"""
|
||||
_logger.debug("Partner enrichment DISABLED - returning empty for domain: %s", company_domain)
|
||||
return {}
|
||||
|
||||
@api.model
|
||||
def read_by_vat(self, vat, timeout=15):
|
||||
"""
|
||||
DISABLED: Return empty data instead of calling Odoo's VAT lookup API.
|
||||
"""
|
||||
_logger.debug("Partner VAT lookup DISABLED - returning empty for VAT: %s", vat)
|
||||
return {}
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
"""Disable company autocomplete features."""
|
||||
_inherit = 'res.company'
|
||||
|
||||
@api.model
|
||||
def autocomplete(self, query, timeout=15):
|
||||
"""
|
||||
DISABLED: Return empty results for company autocomplete.
|
||||
"""
|
||||
_logger.debug("Company autocomplete DISABLED - returning empty results")
|
||||
return []
|
||||
|
||||
82
disable_odoo_online/models/disable_session_leaks.py
Normal file
82
disable_odoo_online/models/disable_session_leaks.py
Normal file
@@ -0,0 +1,82 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Block session-based information leaks and frontend detection mechanisms.
|
||||
Specifically targets the web_enterprise module's subscription checks.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from odoo import api, models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IrHttp(models.AbstractModel):
|
||||
"""
|
||||
Override session info to prevent frontend from detecting license status.
|
||||
This specifically blocks web_enterprise's ExpirationPanel from showing.
|
||||
"""
|
||||
_inherit = 'ir.http'
|
||||
|
||||
def session_info(self):
|
||||
"""
|
||||
Override session info to set permanent valid subscription data.
|
||||
This prevents the frontend ExpirationPanel from showing warnings.
|
||||
|
||||
Key overrides:
|
||||
- expiration_date: Set to far future (2099)
|
||||
- expiration_reason: Set to 'renewal' (valid subscription)
|
||||
- warning: Set to False to hide all warning banners
|
||||
"""
|
||||
result = super().session_info()
|
||||
|
||||
# Override expiration-related session data
|
||||
# These are read by enterprise_subscription_service.js
|
||||
result['expiration_date'] = '2099-12-31 23:59:59'
|
||||
result['expiration_reason'] = 'renewal'
|
||||
result['warning'] = False # Critical: prevents warning banners
|
||||
|
||||
# Remove any "already linked" subscription info
|
||||
# These could trigger redirect prompts
|
||||
result.pop('already_linked_subscription_url', None)
|
||||
result.pop('already_linked_email', None)
|
||||
result.pop('already_linked_send_mail_url', None)
|
||||
|
||||
_logger.debug("Session info patched - expiration set to 2099, warnings disabled")
|
||||
return result
|
||||
|
||||
|
||||
class ResUsers(models.Model):
|
||||
"""
|
||||
Override user creation/modification to prevent subscription checks.
|
||||
When users are created, Odoo Enterprise normally contacts Odoo servers
|
||||
to verify the subscription allows that many users.
|
||||
"""
|
||||
_inherit = 'res.users'
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
"""
|
||||
Override create to ensure no external subscription check is triggered.
|
||||
The actual check happens in publisher_warranty.contract which we've
|
||||
already blocked, but this is an extra safety measure.
|
||||
"""
|
||||
_logger.info("Creating %d user(s) - subscription check DISABLED", len(vals_list))
|
||||
|
||||
# Create users normally - no external checks will happen
|
||||
# because publisher_warranty.contract.update_notification is blocked
|
||||
users = super().create(vals_list)
|
||||
|
||||
# Don't trigger any warranty checks
|
||||
return users
|
||||
|
||||
def write(self, vals):
|
||||
"""
|
||||
Override write to log user modifications.
|
||||
"""
|
||||
result = super().write(vals)
|
||||
|
||||
# If internal user status changed, log it
|
||||
if 'share' in vals or 'groups_id' in vals:
|
||||
_logger.info("User permissions updated - subscription check DISABLED")
|
||||
|
||||
return result
|
||||
Reference in New Issue
Block a user