Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 3 additions & 9 deletions data/config/mosquitto/public/default-dynamic-security.json

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bei Änderungen an den default ACLs bitte immer auch die Rolle mit der Versionsnummer hochsetzen.

Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"anonymousGroup": "anonymous",
"roles": [
{
"rolename": "openwb-version:3",
"rolename": "openwb-version:4",
"textname": "openWB Versionsnummer",
"textdescription": "Diese Rolle ist ein Platzhalter für die openWB Versionsnummer und wird automatisch aktualisiert. Sie hat keine direkten Berechtigungen.",
"acls": []
Expand Down Expand Up @@ -112,13 +112,13 @@
},
{
"acltype": "publishClientReceive",
"topic": "openWB/general/internal_chargepoint/global_data",
"topic": "openWB/internal_chargepoint/global_data",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/general/internal_chargepoint/+/data/parent_cp",
"topic": "openWB/internal_chargepoint/+/data/parent_cp",
"priority": 0,
"allow": true
}
Expand Down Expand Up @@ -1120,12 +1120,6 @@
"topic": "openWB/general/chargemode_config/pv_charging/switch_off_delay",
"priority": 0,
"allow": true
},
{
"acltype": "publishClientReceive",
"topic": "openWB/general/chargemode_config/pv_charging/phases_to_use",
"priority": 0,
"allow": true
}
]
},
Expand Down
2 changes: 0 additions & 2 deletions packages/control/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ class GeneralData:
extern_display_mode: str = field(default="primary", metadata={
"topic": "extern_display_mode"})
extern: bool = field(default=False, metadata={"topic": "extern"})
external_buttons_hw: bool = field(
default=False, metadata={"topic": "external_buttons_hw"})
grid_protection_active: bool = field(
default=False, metadata={"topic": "grid_protection_active"})
grid_protection_configured: bool = field(
Expand Down
193 changes: 193 additions & 0 deletions packages/helpermodules/mosquitto_dynsec/missing_role_topics_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import json
from pathlib import Path
import re

from helpermodules.update_config import UpdateConfig
from test_utils.test_environment import running_on_github


def _get_data_path() -> Path:
if running_on_github():
return Path("/home/runner/work/core/core/data")
else:
return Path(__file__).resolve().parents[3]/"data"


DEFAULT_DYNAMIC_SECURITY_PATH = _get_data_path()/"config/mosquitto/public/default-dynamic-security.json"
ROLE_TEMPLATES_PATH = _get_data_path()/"config/mosquitto/public/role-templates.json"


INTERNAL_TOPICS = ['openWB/bat/set/charging_power_left',
'openWB/bat/set/regulate_up',
'openWB/bat/+/set/power_limit',
'openWB/command/max_id/autolock_plan',
'openWB/command/max_id/charge_template',
'openWB/command/max_id/charge_template_scheduled_plan',
'openWB/command/max_id/charge_template_time_charging_plan',
'openWB/command/max_id/chargepoint_template',
'openWB/command/max_id/device',
'openWB/command/max_id/ev_template',
'openWB/command/max_id/hierarchy',
'openWB/command/max_id/io_action',
'openWB/command/max_id/io_device',
'openWB/command/max_id/mqtt_bridge',
'openWB/command/max_id/vehicle',
'openWB/command/[A-Za-z0-9_]+/error',
'openWB/command/todo',
'openWB/counter/set/disengageable_smarthome_power',
'openWB/counter/set/invalid_home_consumption',
'openWB/counter/set/simulation',
'openWB/counter/+/set/consumption_left',
'openWB/counter/+/set/error_timer',
'openWB/counter/+/set/released_surplus',
'openWB/counter/+/set/reserved_surplus',
'openWB/general/grid_protection_active',
'openWB/general/grid_protection_timestamp',
'openWB/general/grid_protection_random_stop',
'openWB/general/price_kwh', # obsolete topic
'openWB/graph/config/duration',
'openWB/internal_chargepoint/+/data/cp_interruption_duration',
'openWB/internal_chargepoint/+/data/set_current',
'openWB/internal_chargepoint/+/data/phases_to_use',
'openWB/internal_chargepoint/+/get/charge_state',
'openWB/internal_chargepoint/+/get/currents',
'openWB/internal_chargepoint/+/get/current_branch',
'openWB/internal_chargepoint/+/get/current_commit',
'openWB/internal_chargepoint/+/get/error_timestamp',
'openWB/internal_chargepoint/+/get/evse_current',
'openWB/internal_chargepoint/+/get/fault_state',
'openWB/internal_chargepoint/+/get/fault_str',
'openWB/internal_chargepoint/+/get/frequency',
'openWB/internal_chargepoint/+/get/max_evse_current',
'openWB/internal_chargepoint/+/get/plug_state',
'openWB/internal_chargepoint/+/get/phases_in_use',
'openWB/internal_chargepoint/+/get/exported',
'openWB/internal_chargepoint/+/get/imported',
'openWB/internal_chargepoint/+/get/power',
'openWB/internal_chargepoint/+/get/powers',
'openWB/internal_chargepoint/+/get/power_factors',
'openWB/internal_chargepoint/+/get/vehicle_id',
'openWB/internal_chargepoint/+/get/version',
'openWB/internal_chargepoint/+/get/voltages',
'openWB/internal_chargepoint/+/get/serial_number',
'openWB/internal_chargepoint/+/get/soc',
'openWB/internal_chargepoint/+/get/soc_timestamp',
'openWB/internal_chargepoint/+/get/simulation',
'openWB/internal_chargepoint/+/get/state_str',
'openWB/internal_chargepoint/+/get/rfid',
'openWB/internal_chargepoint/+/get/rfid_timestamp',
# abgesehen vom Fahrzeug können mqtt get-Topics bei aktiver BV nur gepublished werden
'openWB/mqtt/bat/+/get/power',
'openWB/mqtt/bat/+/get/soc',
'openWB/mqtt/bat/+/get/imported',
'openWB/mqtt/bat/+/get/exported',
'openWB/mqtt/counter/+/get/currents',
'openWB/mqtt/counter/+/get/imported',
'openWB/mqtt/counter/+/get/exported',
'openWB/mqtt/counter/+/get/power',
'openWB/mqtt/counter/+/get/frequency',
'openWB/mqtt/counter/+/get/power_factors',
'openWB/mqtt/counter/+/get/powers',
'openWB/mqtt/counter/+/get/voltages',
'openWB/mqtt/inverter/+/get/currents',
'openWB/mqtt/inverter/+/get/power',
'openWB/mqtt/inverter/+/get/exported',
'openWB/mqtt/inverter/+/get/dc_power',
'openWB/set/log/request',
'openWB/set/log/data',
'openWB/optional/int_display/detected',
'openWB/optional/int_display/pin_active',
'openWB/optional/int_display/pin_code',
'openWB/system/datastore_version',
'openWB/system/device/+/component/+/simulation',
'openWB/system/device/+/component/+/simulation/power_present',
'openWB/system/device/+/component/+/simulation/present_exported',
'openWB/system/device/+/component/+/simulation/present_imported',
'openWB/system/device/+/component/+/simulation/timestamp_present',
'openWB/system/device/module_update_completed',
'openWB/system/hostname',
'openWB/system/lastlivevaluesJson',
'openWB/system/release_train']

NOT_PERSISTENT_TOPICS = ['openWB/system/messages/[^/]+',
'openWB/system/messages/.*',
'openWB/command/[^/]+/messages/[^/]+',
'openWB/command/[^/]+/messages/.*',
'openWB/system/serial_number',
'openWB/system/mac_address',
'openWB/optional/dc_charging',
'others/.*',
'$CONTROL/dynamic-security/.*',
'$SYS/.*',
'openWB/simpleAPI/set/.*',
'openWB-remote/.*',
'openWB/log/[^/]+/data',
'openWB/log/daily/.*',
'openWB/log/monthly/.*',
'openWB/log/yearly/.*',
'$CONTROL/dynamic-security/v1',
'$CONTROL/dynamic-security/v1/response',
]


def test_valid_topics_vs_role_topics():
def parse_acl_topic_to_regex(acl_topic):
if "openWB/set" not in acl_topic and "#" != acl_topic and "openWB/#" != acl_topic:
# convert mqtt topic pattern to regex pattern
topic_regex = acl_topic.replace("+", "[^/]+").replace("#", ".*").replace("<id>", "[^/]+")
return topic_regex
return None
Comment on lines +134 to +139
# setup
valid_topics = list(UpdateConfig.valid_topic)
for i, valid_topic in enumerate(valid_topics):
if valid_topic.startswith("^"):
valid_topic = valid_topic[1:]
if valid_topic.endswith("$"):
valid_topic = valid_topic[:-1]
valid_topic = re.sub(r"\[0-9]\+", "+", valid_topic)
valid_topic = re.sub(r"\[0-1]", "+", valid_topic)
valid_topic = re.sub(r"\[0-9]", "+", valid_topic)
valid_topics[i] = valid_topic

with open(DEFAULT_DYNAMIC_SECURITY_PATH, "r") as f:
security = json.load(f)
role_topics = []
for role in security.get("roles", []):
for acl in role.get("acls", []):
if "topic" in acl:
topic_regex = parse_acl_topic_to_regex(acl["topic"])
if topic_regex:
role_topics.append(topic_regex)
with open(ROLE_TEMPLATES_PATH, "r") as f:
roles = json.load(f)
for role in roles:
for acl in role.get("acls", []):
if "topic" in acl:
topic_regex = parse_acl_topic_to_regex(acl["topic"])
if topic_regex:
role_topics.append(topic_regex)
# execution
# prüfen, ob alle valid_topics in den role_topics vorkommen, damit neue Topics nicht in der BV vergessen werden
missing_valid_topic = []
for valid_topic in valid_topics:
found = False
for role_topic in role_topics:
if re.fullmatch(role_topic, valid_topic) or valid_topic in INTERNAL_TOPICS:
found = True
break
if not found:
missing_valid_topic.append(valid_topic)
# prüfen, ob alle role_topics in den valid_topics vorkommen, damit keine ungültigen Topics in der BV sind
missing_role_topic = []
for role_topic in role_topics:
found = False
for valid_topic in valid_topics:
if re.fullmatch(role_topic, valid_topic) or role_topic in NOT_PERSISTENT_TOPICS:
found = True
break
if not found:
missing_role_topic.append(role_topic)

# assertion
assert len(missing_valid_topic) == 0, f"Fehlende Topics in der Benutzerverwaltung: {missing_valid_topic}"
assert len(missing_role_topic) == 0, f"Obsolete Topics in der Benutzerverwaltung: {missing_role_topic}"
14 changes: 1 addition & 13 deletions packages/helpermodules/setdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,8 +759,6 @@ def process_general_topic(self, msg: mqtt.MQTTMessage):
self._validate_value(msg, bool)
elif "openWB/set/general/control_interval" in msg.topic:
self._validate_value(msg, int, [(10, 10), (20, 20), (60, 60)])
elif "openWB/set/general/external_buttons_hw" in msg.topic:
self._validate_value(msg, bool)
elif "openWB/set/general/chargemode_config/unbalanced_load_limit" in msg.topic:
self._validate_value(msg, int, [(10, 32)])
elif ("openWB/set/general/chargemode_config/unbalanced_load" in msg.topic or
Expand Down Expand Up @@ -790,22 +788,12 @@ def process_general_topic(self, msg: mqtt.MQTTMessage):
elif "openWB/set/general/chargemode_config/" in msg.topic and "/phases_to_use" in msg.topic:
self._validate_value(msg, int, [(1, 1), (3, 3)])
elif ("openWB/set/general/grid_protection_configured" in msg.topic or
"openWB/set/general/grid_protection_active" in msg.topic or
"openWB/set/general/mqtt_bridge" in msg.topic):
"openWB/set/general/grid_protection_active" in msg.topic):
self._validate_value(msg, bool)
elif "openWB/set/general/grid_protection_timestamp" in msg.topic:
self._validate_value(msg, float, [(0, TIMESTAMP_2100)])
elif "openWB/set/general/grid_protection_random_stop" in msg.topic:
self._validate_value(msg, int, [(0, 90)])
elif "openWB/set/general/notifications/selected" in msg.topic:
self._validate_value(msg, str)
elif "openWB/set/general/notifications/configuration" in msg.topic:
self._validate_value(msg, "json")
elif ("openWB/set/general/notifications/start_charging" in msg.topic or
"openWB/set/general/notifications/stop_charging" in msg.topic or
"openWB/set/general/notifications/plug" in msg.topic or
"openWB/set/general/notifications/smart_home" in msg.topic):
self._validate_value(msg, bool)
elif ("openWB/set/general/prices/bat" in msg.topic or
"openWB/set/general/prices/grid" in msg.topic or
"openWB/set/general/prices/pv" in msg.topic):
Expand Down
41 changes: 24 additions & 17 deletions packages/helpermodules/update_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,22 +218,14 @@ class UpdateConfig:
"^openWB/general/extern_display_mode$",
"^openWB/general/charge_log_data_config$",
"^openWB/general/control_interval$",
"^openWB/general/external_buttons_hw$",
"^openWB/general/grid_protection_configured$",
"^openWB/general/grid_protection_active$",
"^openWB/general/http_api$",
"^openWB/general/modbus_control$",
"^openWB/general/mqtt_bridge$",
"^openWB/general/grid_protection_timestamp$",
"^openWB/general/grid_protection_random_stop$",
"^openWB/general/range_unit$",
"^openWB/general/temporary_charge_templates_active$",
"^openWB/general/notifications/selected$",
"^openWB/general/notifications/configuration$",
"^openWB/general/notifications/start_charging$",
"^openWB/general/notifications/stop_charging$",
"^openWB/general/notifications/plug$",
"^openWB/general/notifications/smart_home$",
"^openWB/general/chargemode_config/unbalanced_load_limit$",
"^openWB/general/chargemode_config/unbalanced_load$",
"^openWB/general/chargemode_config/pv_charging/bat_mode$",
Expand Down Expand Up @@ -263,7 +255,6 @@ class UpdateConfig:
"^openWB/graph/lastlivevaluesJson$",

"^openWB/internal_chargepoint/global_data$",
"^openWB/internal_chargepoint/global_config$",
"^openWB/internal_chargepoint/[0-1]/data/cp_interruption_duration$",
"^openWB/internal_chargepoint/[0-1]/data/set_current$",
"^openWB/internal_chargepoint/[0-1]/data/phases_to_use$",
Expand Down Expand Up @@ -298,11 +289,32 @@ class UpdateConfig:

"^openWB/io/action/[0-9]+/config$",
"^openWB/io/action/[0-9]+/timestamp$",
"^openWB/io/states/[0-9]+/get/analog_input$",
"^openWB/io/states/[0-9]+/get/analog_output$",
"^openWB/io/states/[0-9]+/get/digital_input$",
"^openWB/io/states/[0-9]+/get/digital_output$",
"^openWB/io/states/[0-9]+/set/analog_input$",
"^openWB/io/states/[0-9]+/set/analog_output$",
"^openWB/io/states/[0-9]+/set/digital_input$",
"^openWB/io/states/[0-9]+/set/digital_output$",

"^openWB/mqtt/bat/[0-9]+/get/power$",
"^openWB/mqtt/bat/[0-9]+/get/soc$",
"^openWB/mqtt/bat/[0-9]+/get/imported$",
"^openWB/mqtt/bat/[0-9]+/get/exported$",
"^openWB/mqtt/bat/[0-9]+/set/power_limit$",
"^openWB/mqtt/chargepoint/[0-9]+/set/current$",
"^openWB/mqtt/chargepoint/[0-9]+/set/phases_to_use$",
"^openWB/mqtt/chargepoint/[0-9]+/get/currents$",
"^openWB/mqtt/chargepoint/[0-9]+/get/power$",
"^openWB/mqtt/chargepoint/[0-9]+/get/plug_state$",
"^openWB/mqtt/chargepoint/[0-9]+/get/charge_state$",
"^openWB/mqtt/chargepoint/[0-9]+/get/phases_in_use$",
"^openWB/mqtt/chargepoint/[0-9]+/get/imported$",
"^openWB/mqtt/chargepoint/[0-9]+/get/exported$",
"^openWB/mqtt/chargepoint/[0-9]+/get/voltages$",
"^openWB/mqtt/chargepoint/[0-9]+/get/power_factors$",
"^openWB/mqtt/chargepoint/[0-9]+/get/rfid$",
"^openWB/mqtt/counter/[0-9]+/get/currents$",
"^openWB/mqtt/counter/[0-9]+/get/imported$",
"^openWB/mqtt/counter/[0-9]+/get/exported$",
Expand All @@ -322,6 +334,8 @@ class UpdateConfig:
"^openWB/set/log/request",
"^openWB/set/log/data",

"^openWB/optional/ep/configured$",
"^openWB/optional/ep/get/prices$",
"^openWB/optional/ep/flexible_tariff/get/fault_state$",
"^openWB/optional/ep/flexible_tariff/get/fault_str$",
"^openWB/optional/ep/flexible_tariff/get/prices$",
Expand All @@ -338,7 +352,7 @@ class UpdateConfig:
"^openWB/optional/int_display/standby$",
"^openWB/optional/int_display/rotation$",
"^openWB/optional/int_display/theme$",
"^openWB/optional/int_display/only_local_charge_points",
"^openWB/optional/int_display/only_local_charge_points$",
"^openWB/optional/monitoring/config$",
"^openWB/optional/rfid/active$",
"^openWB/optional/ocpp/config$",
Expand Down Expand Up @@ -605,16 +619,9 @@ class UpdateConfig:
("openWB/general/control_interval", 10),
("openWB/general/extern", False),
("openWB/general/extern_display_mode", "primary"),
("openWB/general/external_buttons_hw", False),
("openWB/general/grid_protection_configured", True),
("openWB/general/http_api", False),
("openWB/general/modbus_control", False),
("openWB/general/notifications/selected", "none"),
("openWB/general/notifications/plug", False),
("openWB/general/notifications/start_charging", False),
("openWB/general/notifications/stop_charging", False),
("openWB/general/notifications/smart_home", False),
("openWB/general/notifications/configuration", {}),
("openWB/general/prices/bat", Prices().bat),
("openWB/general/prices/grid", Prices().grid),
("openWB/general/prices/pv", Prices().pv),
Expand Down
Loading