2018-07-13 13:31:20 +00:00
|
|
|
"""Offer API to configure Home Assistant auth."""
|
|
|
|
import voluptuous as vol
|
|
|
|
|
|
|
|
from homeassistant.components import websocket_api
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
WS_TYPE_LIST = "config/auth/list"
|
|
|
|
SCHEMA_WS_LIST = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
|
|
|
{vol.Required("type"): WS_TYPE_LIST}
|
|
|
|
)
|
2018-07-13 13:31:20 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
WS_TYPE_DELETE = "config/auth/delete"
|
|
|
|
SCHEMA_WS_DELETE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
|
|
|
{vol.Required("type"): WS_TYPE_DELETE, vol.Required("user_id"): str}
|
|
|
|
)
|
2018-07-13 13:31:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def async_setup(hass):
|
|
|
|
"""Enable the Home Assistant views."""
|
2022-01-11 17:24:08 +00:00
|
|
|
websocket_api.async_register_command(
|
|
|
|
hass, WS_TYPE_LIST, websocket_list, SCHEMA_WS_LIST
|
2018-07-13 13:31:20 +00:00
|
|
|
)
|
2022-01-11 17:24:08 +00:00
|
|
|
websocket_api.async_register_command(
|
|
|
|
hass, WS_TYPE_DELETE, websocket_delete, SCHEMA_WS_DELETE
|
2018-07-13 13:31:20 +00:00
|
|
|
)
|
2022-01-11 17:24:08 +00:00
|
|
|
websocket_api.async_register_command(hass, websocket_create)
|
|
|
|
websocket_api.async_register_command(hass, websocket_update)
|
2018-07-13 13:31:20 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
|
2018-12-14 09:19:27 +00:00
|
|
|
@websocket_api.require_admin
|
2018-10-03 05:53:54 +00:00
|
|
|
@websocket_api.async_response
|
|
|
|
async def websocket_list(hass, connection, msg):
|
2018-07-13 13:31:20 +00:00
|
|
|
"""Return a list of users."""
|
2018-10-03 05:53:54 +00:00
|
|
|
result = [_user_info(u) for u in await hass.auth.async_get_users()]
|
2018-07-13 13:31:20 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
connection.send_message(websocket_api.result_message(msg["id"], result))
|
2018-07-13 13:31:20 +00:00
|
|
|
|
|
|
|
|
2018-12-14 09:19:27 +00:00
|
|
|
@websocket_api.require_admin
|
2018-10-03 05:53:54 +00:00
|
|
|
@websocket_api.async_response
|
|
|
|
async def websocket_delete(hass, connection, msg):
|
2018-07-13 13:31:20 +00:00
|
|
|
"""Delete a user."""
|
2019-07-31 19:25:30 +00:00
|
|
|
if msg["user_id"] == connection.user.id:
|
|
|
|
connection.send_message(
|
|
|
|
websocket_api.error_message(
|
|
|
|
msg["id"], "no_delete_self", "Unable to delete your own account"
|
|
|
|
)
|
|
|
|
)
|
2018-10-03 05:53:54 +00:00
|
|
|
return
|
2018-07-13 13:31:20 +00:00
|
|
|
|
2021-10-31 17:35:27 +00:00
|
|
|
if not (user := await hass.auth.async_get_user(msg["user_id"])):
|
2019-07-31 19:25:30 +00:00
|
|
|
connection.send_message(
|
|
|
|
websocket_api.error_message(msg["id"], "not_found", "User not found")
|
|
|
|
)
|
2018-10-03 05:53:54 +00:00
|
|
|
return
|
2018-07-13 13:31:20 +00:00
|
|
|
|
2018-10-03 05:53:54 +00:00
|
|
|
await hass.auth.async_remove_user(user)
|
2018-07-13 13:31:20 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
connection.send_message(websocket_api.result_message(msg["id"]))
|
2018-07-13 13:31:20 +00:00
|
|
|
|
|
|
|
|
2018-12-14 09:19:27 +00:00
|
|
|
@websocket_api.require_admin
|
2018-10-03 05:53:54 +00:00
|
|
|
@websocket_api.async_response
|
2020-03-30 18:33:43 +00:00
|
|
|
@websocket_api.websocket_command(
|
|
|
|
{
|
|
|
|
vol.Required("type"): "config/auth/create",
|
|
|
|
vol.Required("name"): str,
|
|
|
|
vol.Optional("group_ids"): [str],
|
2021-11-29 22:01:03 +00:00
|
|
|
vol.Optional("local_only"): bool,
|
2020-03-30 18:33:43 +00:00
|
|
|
}
|
|
|
|
)
|
2018-10-03 05:53:54 +00:00
|
|
|
async def websocket_create(hass, connection, msg):
|
2018-07-13 13:31:20 +00:00
|
|
|
"""Create a user."""
|
2021-11-29 22:01:03 +00:00
|
|
|
user = await hass.auth.async_create_user(
|
|
|
|
msg["name"], group_ids=msg.get("group_ids"), local_only=msg.get("local_only")
|
|
|
|
)
|
2018-07-13 13:31:20 +00:00
|
|
|
|
2018-10-03 05:53:54 +00:00
|
|
|
connection.send_message(
|
2019-07-31 19:25:30 +00:00
|
|
|
websocket_api.result_message(msg["id"], {"user": _user_info(user)})
|
|
|
|
)
|
2018-07-13 13:31:20 +00:00
|
|
|
|
|
|
|
|
2019-03-11 19:08:02 +00:00
|
|
|
@websocket_api.require_admin
|
|
|
|
@websocket_api.async_response
|
2019-07-31 19:25:30 +00:00
|
|
|
@websocket_api.websocket_command(
|
|
|
|
{
|
|
|
|
vol.Required("type"): "config/auth/update",
|
|
|
|
vol.Required("user_id"): str,
|
|
|
|
vol.Optional("name"): str,
|
2020-11-27 08:13:16 +00:00
|
|
|
vol.Optional("is_active"): bool,
|
2019-07-31 19:25:30 +00:00
|
|
|
vol.Optional("group_ids"): [str],
|
2021-11-29 22:01:03 +00:00
|
|
|
vol.Optional("local_only"): bool,
|
2019-07-31 19:25:30 +00:00
|
|
|
}
|
|
|
|
)
|
2019-03-11 19:08:02 +00:00
|
|
|
async def websocket_update(hass, connection, msg):
|
|
|
|
"""Update a user."""
|
2021-10-31 17:35:27 +00:00
|
|
|
if not (user := await hass.auth.async_get_user(msg.pop("user_id"))):
|
2019-07-31 19:25:30 +00:00
|
|
|
connection.send_message(
|
|
|
|
websocket_api.error_message(
|
|
|
|
msg["id"], websocket_api.const.ERR_NOT_FOUND, "User not found"
|
|
|
|
)
|
|
|
|
)
|
2019-03-11 19:08:02 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
if user.system_generated:
|
2019-07-31 19:25:30 +00:00
|
|
|
connection.send_message(
|
|
|
|
websocket_api.error_message(
|
|
|
|
msg["id"],
|
|
|
|
"cannot_modify_system_generated",
|
|
|
|
"Unable to update system generated users.",
|
|
|
|
)
|
|
|
|
)
|
2019-03-11 19:08:02 +00:00
|
|
|
return
|
|
|
|
|
2021-05-04 21:26:48 +00:00
|
|
|
if user.is_owner and msg.get("is_active") is False:
|
2020-11-27 08:13:16 +00:00
|
|
|
connection.send_message(
|
|
|
|
websocket_api.error_message(
|
|
|
|
msg["id"],
|
|
|
|
"cannot_deactivate_owner",
|
|
|
|
"Unable to deactivate owner.",
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
msg.pop("type")
|
|
|
|
msg_id = msg.pop("id")
|
2019-03-11 19:08:02 +00:00
|
|
|
|
|
|
|
await hass.auth.async_update_user(user, **msg)
|
|
|
|
|
|
|
|
connection.send_message(
|
2019-07-31 19:25:30 +00:00
|
|
|
websocket_api.result_message(msg_id, {"user": _user_info(user)})
|
|
|
|
)
|
2019-03-11 19:08:02 +00:00
|
|
|
|
|
|
|
|
2018-07-13 13:31:20 +00:00
|
|
|
def _user_info(user):
|
|
|
|
"""Format a user."""
|
2020-11-20 14:42:19 +00:00
|
|
|
|
|
|
|
ha_username = next(
|
|
|
|
(
|
|
|
|
cred.data.get("username")
|
|
|
|
for cred in user.credentials
|
|
|
|
if cred.auth_provider_type == "homeassistant"
|
|
|
|
),
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
|
2018-07-13 13:31:20 +00:00
|
|
|
return {
|
2019-07-31 19:25:30 +00:00
|
|
|
"id": user.id,
|
2020-11-20 14:42:19 +00:00
|
|
|
"username": ha_username,
|
2019-07-31 19:25:30 +00:00
|
|
|
"name": user.name,
|
|
|
|
"is_owner": user.is_owner,
|
|
|
|
"is_active": user.is_active,
|
2021-12-03 15:34:26 +00:00
|
|
|
"local_only": user.local_only,
|
2019-07-31 19:25:30 +00:00
|
|
|
"system_generated": user.system_generated,
|
|
|
|
"group_ids": [group.id for group in user.groups],
|
|
|
|
"credentials": [{"type": c.auth_provider_type} for c in user.credentials],
|
2018-07-13 13:31:20 +00:00
|
|
|
}
|