Allow integration to run with limited access rights #235

This commit is contained in:
Tomaae 2022-08-20 01:42:03 +02:00
parent ac789a81c2
commit 58f931ad8e
No known key found for this signature in database
GPG key ID: 8360BBD8A381D1C0
3 changed files with 110 additions and 4 deletions

View file

@ -174,9 +174,9 @@ Use integration master branch instead of latest release to keep up with RouterOS
## Setup integration ## Setup integration
1. Create user for homeassistant on your mikrotik router with following permissions: 1. Create user for homeassistant on your mikrotik router with following permissions:
* read, write, api, test, policy * read, write, api, reboot, policy
2. If you want to be able to execute scripts on your mikrotik router from HA, script needs to have only following policies: 2. If you want to be able to execute scripts on your mikrotik router from HA, script needs to have only following policies:
* read, write, test * read, write
or check "Don't Require Permissions" option or check "Don't Require Permissions" option
3. Setup this integration for your Mikrotik device in Home Assistant via `Configuration -> Integrations -> Add -> Mikrotik Router`. 3. Setup this integration for your Mikrotik device in Home Assistant via `Configuration -> Integrations -> Add -> Mikrotik Router`.
You can add this integration several times for different devices. You can add this integration several times for different devices.

View file

@ -106,6 +106,7 @@ class MikrotikControllerData:
self.host = config_entry.data[CONF_HOST] self.host = config_entry.data[CONF_HOST]
self.data = { self.data = {
"access": {},
"routerboard": {}, "routerboard": {},
"resource": {}, "resource": {},
"health": {}, "health": {},
@ -181,7 +182,7 @@ class MikrotikControllerData:
self.major_fw_version = 0 self.major_fw_version = 0
self.async_mac_lookup = AsyncMacLookup() self.async_mac_lookup = AsyncMacLookup()
# self.async_mac_lookup.update_vendors() self.accessrights_reported = False
async def async_init(self): async def async_init(self):
self.listeners.append( self.listeners.append(
@ -491,7 +492,10 @@ class MikrotikControllerData:
except Exception: except Exception:
return return
await self.hass.async_add_executor_job(self.get_firmware_update) await self.hass.async_add_executor_job(self.get_access)
if self.api.connected():
await self.hass.async_add_executor_job(self.get_firmware_update)
if self.api.connected(): if self.api.connected():
await self.hass.async_add_executor_job(self.get_system_resource) await self.hass.async_add_executor_job(self.get_system_resource)
@ -685,6 +689,48 @@ class MikrotikControllerData:
async_dispatcher_send(self.hass, self.signal_update) async_dispatcher_send(self.hass, self.signal_update)
self.lock.release() self.lock.release()
# ---------------------------
# get_access
# ---------------------------
def get_access(self):
"""Get access rights from Mikrotik"""
tmp_user = parse_api(
data={},
source=self.api.query("/user"),
key="name",
vals=[
{"name": "name"},
{"name": "group"},
],
)
tmp_group = parse_api(
data={},
source=self.api.query("/user/group"),
key="name",
vals=[
{"name": "name"},
{"name": "policy"},
],
)
self.data["access"] = tmp_group[
tmp_user[self.config_entry.data[CONF_USERNAME]]["group"]
]["policy"].split(",")
if not self.accessrights_reported:
self.accessrights_reported = True
if (
"write" not in self.data["access"]
or "policy" not in self.data["access"]
or "reboot" not in self.data["access"]
):
_LOGGER.warning(
"Mikrotik %s user %s does not have sufficient access rights. Integration functionality will be limited.",
self.host,
self.config_entry.data[CONF_USERNAME],
)
# --------------------------- # ---------------------------
# get_interface # get_interface
# --------------------------- # ---------------------------
@ -1330,11 +1376,26 @@ class MikrotikControllerData:
], ],
) )
if (
"write" not in self.data["access"]
or "policy" not in self.data["access"]
or "reboot" not in self.data["access"]
):
self.data["routerboard"].pop("current-firmware")
self.data["routerboard"].pop("upgrade-firmware")
# --------------------------- # ---------------------------
# get_system_health # get_system_health
# --------------------------- # ---------------------------
def get_system_health(self): def get_system_health(self):
"""Get routerboard data from Mikrotik""" """Get routerboard data from Mikrotik"""
if (
"write" not in self.data["access"]
or "policy" not in self.data["access"]
or "reboot" not in self.data["access"]
):
return
if 0 < self.major_fw_version < 7: if 0 < self.major_fw_version < 7:
self.data["health"] = parse_api( self.data["health"] = parse_api(
data=self.data["health"], data=self.data["health"],
@ -1467,6 +1528,9 @@ class MikrotikControllerData:
# --------------------------- # ---------------------------
def get_firmware_update(self): def get_firmware_update(self):
"""Check for firmware update on Mikrotik""" """Check for firmware update on Mikrotik"""
if "write" not in self.data["access"] or "policy" not in self.data["access"]:
return
self.execute("/system/package/update", "check-for-updates", None, None) self.execute("/system/package/update", "check-for-updates", None, None)
self.data["fw-update"] = parse_api( self.data["fw-update"] = parse_api(
data=self.data["fw-update"], data=self.data["fw-update"],

View file

@ -70,6 +70,9 @@ class MikrotikSwitch(MikrotikEntity, SwitchEntity, RestoreEntity):
async def async_turn_on(self) -> None: async def async_turn_on(self) -> None:
"""Turn on the switch.""" """Turn on the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = self.entity_description.data_reference param = self.entity_description.data_reference
value = self._data[self.entity_description.data_reference] value = self._data[self.entity_description.data_reference]
@ -79,6 +82,9 @@ class MikrotikSwitch(MikrotikEntity, SwitchEntity, RestoreEntity):
async def async_turn_off(self) -> None: async def async_turn_off(self) -> None:
"""Turn off the switch.""" """Turn off the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = self.entity_description.data_reference param = self.entity_description.data_reference
value = self._data[self.entity_description.data_reference] value = self._data[self.entity_description.data_reference]
@ -125,6 +131,9 @@ class MikrotikPortSwitch(MikrotikSwitch):
async def async_turn_on(self) -> Optional[str]: async def async_turn_on(self) -> Optional[str]:
"""Turn on the switch.""" """Turn on the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = self.entity_description.data_reference param = self.entity_description.data_reference
if self._data["about"] == "managed by CAPsMAN": if self._data["about"] == "managed by CAPsMAN":
@ -144,6 +153,9 @@ class MikrotikPortSwitch(MikrotikSwitch):
async def async_turn_off(self) -> Optional[str]: async def async_turn_off(self) -> Optional[str]:
"""Turn off the switch.""" """Turn off the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = self.entity_description.data_reference param = self.entity_description.data_reference
if self._data["about"] == "managed by CAPsMAN": if self._data["about"] == "managed by CAPsMAN":
@ -170,6 +182,9 @@ class MikrotikNATSwitch(MikrotikSwitch):
async def async_turn_on(self) -> None: async def async_turn_on(self) -> None:
"""Turn on the switch.""" """Turn on the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = ".id" param = ".id"
value = None value = None
@ -187,6 +202,9 @@ class MikrotikNATSwitch(MikrotikSwitch):
async def async_turn_off(self) -> None: async def async_turn_off(self) -> None:
"""Turn off the switch.""" """Turn off the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = ".id" param = ".id"
value = None value = None
@ -211,6 +229,9 @@ class MikrotikMangleSwitch(MikrotikSwitch):
async def async_turn_on(self) -> None: async def async_turn_on(self) -> None:
"""Turn on the switch.""" """Turn on the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = ".id" param = ".id"
value = None value = None
@ -229,6 +250,9 @@ class MikrotikMangleSwitch(MikrotikSwitch):
async def async_turn_off(self) -> None: async def async_turn_off(self) -> None:
"""Turn off the switch.""" """Turn off the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = ".id" param = ".id"
value = None value = None
@ -254,6 +278,9 @@ class MikrotikFilterSwitch(MikrotikSwitch):
async def async_turn_on(self) -> None: async def async_turn_on(self) -> None:
"""Turn on the switch.""" """Turn on the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = ".id" param = ".id"
value = None value = None
@ -271,6 +298,9 @@ class MikrotikFilterSwitch(MikrotikSwitch):
async def async_turn_off(self) -> None: async def async_turn_off(self) -> None:
"""Turn off the switch.""" """Turn off the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = ".id" param = ".id"
value = None value = None
@ -295,6 +325,9 @@ class MikrotikQueueSwitch(MikrotikSwitch):
async def async_turn_on(self) -> None: async def async_turn_on(self) -> None:
"""Turn on the switch.""" """Turn on the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = ".id" param = ".id"
value = None value = None
@ -308,6 +341,9 @@ class MikrotikQueueSwitch(MikrotikSwitch):
async def async_turn_off(self) -> None: async def async_turn_off(self) -> None:
"""Turn off the switch.""" """Turn off the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = ".id" param = ".id"
value = None value = None
@ -328,6 +364,9 @@ class MikrotikKidcontrolPauseSwitch(MikrotikSwitch):
async def async_turn_on(self) -> None: async def async_turn_on(self) -> None:
"""Turn on the switch.""" """Turn on the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = self.entity_description.data_reference param = self.entity_description.data_reference
value = self._data[self.entity_description.data_reference] value = self._data[self.entity_description.data_reference]
@ -337,6 +376,9 @@ class MikrotikKidcontrolPauseSwitch(MikrotikSwitch):
async def async_turn_off(self) -> None: async def async_turn_off(self) -> None:
"""Turn off the switch.""" """Turn off the switch."""
if "write" not in self._ctrl.data["access"]:
return
path = self.entity_description.data_switch_path path = self.entity_description.data_switch_path
param = self.entity_description.data_reference param = self.entity_description.data_reference
value = self._data[self.entity_description.data_reference] value = self._data[self.entity_description.data_reference]