ok
Direktori : /opt/imunify360/venv/lib/python3.11/site-packages/im360/simple_rpc/ |
Current File : //opt/imunify360/venv/lib/python3.11/site-packages/im360/simple_rpc/disabled_rules.py |
import asyncio import functools import time from peewee import DoesNotExist, IntegrityError from defence360agent.model.simplification import run_in_executor from defence360agent.rpc_tools import ValidationError, lookup from defence360agent.rpc_tools.utils import run_in_executor_decorator from defence360agent.utils import Scope from im360.contracts.config import Modsec, Permissions from defence360agent.contracts.messages import MessageType from im360.model.incident import DisabledRule, DisabledRuleDomain from im360.subsys import ( waf_rules_configurator, modsec_app_version_detector, ) from defence360agent.subsys import web_server from im360.subsys.panels import hosting_panel from im360.subsys.panels.generic.mod_security import ( GenericPanelModSecException, ) class DisabledRulesEndpoints(lookup.RootEndpoints): SCOPE = Scope.IM360 def __init__(self, sink): super().__init__(sink) self.hp = hosting_panel.HostingPanel() @lookup.bind("rules", "disable") async def disable_rule(self, plugin, id, name, domains=None): self.__check_edit_is_enabled() domains = domains or [] if domains and plugin != "modsec": raise ValidationError("Domains only allowed for plugin=modsec") # validate domain panel_domains = set(await self.hp.get_user_domains()) if not set(domains).issubset(panel_domains): raise ValidationError( "Some of the provided domains do not exist: {}".format( set(domains) - panel_domains, ) ) # NOTE: we can't call _store_disabled_rule after # _sync_modsec_configs, because we need to form a union of # specified domains and domains for which the specified rule is # already disabled. We might refactor this method in DEF-10761. sync_domains = await self._store_disabled_rule( plugin, id, name, domains ) if plugin == "modsec": await self._sync_modsec_configs(set(sync_domains) & panel_domains) await self._sink.process_message( MessageType.RuleDisabled( plugin_id=plugin, rule=id, name=name, domains=(domains or None), timestamp=time.time(), ) ) async def _delete_disabled_rule(self, plugin, id): loop = asyncio.get_event_loop() await run_in_executor( loop, lambda: DisabledRule.delete() .where(DisabledRule.plugin == plugin, DisabledRule.rule_id == id) .execute(), ) @lookup.bind("rules", "enable") async def enable_rule(self, plugin, id): self.__check_edit_is_enabled() loop = asyncio.get_event_loop() try: dr = await run_in_executor( loop, lambda: DisabledRule.get(plugin=plugin, rule_id=id) ) except DoesNotExist: return if plugin == "modsec": domains = [ d[0] for d in await run_in_executor( loop, lambda: DisabledRuleDomain.select( DisabledRuleDomain.domain ) .where(DisabledRuleDomain.disabled_rule_id_id == dr.id) .tuples(), ) ] await self._delete_disabled_rule(plugin, id) panel_domains = set(await self.hp.get_user_domains()) await self._sync_modsec_configs(set(domains) & panel_domains) else: await self._delete_disabled_rule(plugin, id) await self._sink.process_message( MessageType.RuleEnabled( plugin_id=plugin, rule=id, timestamp=time.time() ) ) @lookup.bind("rules", "list-disabled") @run_in_executor_decorator def list_disabled_rules(self, limit, offset, order_by=None): return DisabledRule.fetch(limit, offset, order_by) @lookup.bind("rules", "update-app-specific-rules") async def update_app_based_rules(self): if not Modsec.APP_SPECIFIC_RULESET: raise ValidationError("App specific ruleset setting is disabled.") try: await waf_rules_configurator.update_waf_rules_config() except ( waf_rules_configurator.NotSupportedWebserverError, modsec_app_version_detector.DatabaseNotFoundError, NotImplementedError, ) as e: raise ValidationError(str(e)) @run_in_executor_decorator def _store_disabled_rule(self, plugin, id, name, domains): sync_domains = set(domains) try: inserted_id = DisabledRule.insert( plugin=plugin, rule_id=id, name=name ).execute() except IntegrityError: dr = DisabledRule.get(plugin=plugin, rule_id=id) for d in DisabledRuleDomain.select().where( DisabledRuleDomain.disabled_rule_id_id == dr.id ): sync_domains.add(d.domain) DisabledRuleDomain.delete().where( DisabledRuleDomain.disabled_rule_id_id == dr.id ).execute() for d in domains: DisabledRuleDomain.create_or_get( disabled_rule_id_id=dr.id, domain=d ) else: for d in domains: DisabledRuleDomain.create( disabled_rule_id_id=inserted_id, domain=d ) return list(sync_domains) async def _sync_modsec_configs(self, domains: set): loop = asyncio.get_event_loop() domain_list = list(domains) rules_list = await asyncio.gather( *( run_in_executor( loop, functools.partial( DisabledRule.get_domain_disabled, "modsec", d ), ) for d in domain_list ) ) try: if domain_list: await self.hp.sync_disabled_rules_for_domains( dict(zip(domain_list, rules_list)) ) except GenericPanelModSecException as e: # don't send errors from generic panel to Sentry; # panel admin is responsible for configuring generic panel raise ValidationError(str(e)) from e rules = await run_in_executor( loop, lambda: DisabledRule.get_global_disabled("modsec") ) await self.hp.sync_global_disabled_rules(rules) await web_server.graceful_restart() @staticmethod def __check_edit_is_enabled(): if not Permissions.ALLOW_LOCAL_RULES_MANAGEMENT: raise ValidationError("Local rule management is disabled.")