diff --git a/homeassistant/auth/__init__.py b/homeassistant/auth/__init__.py index 710b4af1cd85..26bd10535d0e 100644 --- a/homeassistant/auth/__init__.py +++ b/homeassistant/auth/__init__.py @@ -215,12 +215,14 @@ class AuthManager: return user - async def async_create_user(self, name: str) -> models.User: + async def async_create_user( + self, name: str, group_ids: Optional[List[str]] = None + ) -> models.User: """Create a user.""" kwargs: Dict[str, Any] = { "name": name, "is_active": True, - "group_ids": [GROUP_ID_ADMIN], + "group_ids": group_ids or [], } if await self._user_should_be_owner(): diff --git a/homeassistant/components/config/auth.py b/homeassistant/components/config/auth.py index 361367ffb4d5..d5bbb60e27de 100644 --- a/homeassistant/components/config/auth.py +++ b/homeassistant/components/config/auth.py @@ -13,11 +13,6 @@ SCHEMA_WS_DELETE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( {vol.Required("type"): WS_TYPE_DELETE, vol.Required("user_id"): str} ) -WS_TYPE_CREATE = "config/auth/create" -SCHEMA_WS_CREATE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( - {vol.Required("type"): WS_TYPE_CREATE, vol.Required("name"): str} -) - async def async_setup(hass): """Enable the Home Assistant views.""" @@ -27,9 +22,7 @@ async def async_setup(hass): hass.components.websocket_api.async_register_command( WS_TYPE_DELETE, websocket_delete, SCHEMA_WS_DELETE ) - hass.components.websocket_api.async_register_command( - WS_TYPE_CREATE, websocket_create, SCHEMA_WS_CREATE - ) + hass.components.websocket_api.async_register_command(websocket_create) hass.components.websocket_api.async_register_command(websocket_update) return True @@ -70,9 +63,16 @@ async def websocket_delete(hass, connection, msg): @websocket_api.require_admin @websocket_api.async_response +@websocket_api.websocket_command( + { + vol.Required("type"): "config/auth/create", + vol.Required("name"): str, + vol.Optional("group_ids"): [str], + } +) async def websocket_create(hass, connection, msg): """Create a user.""" - user = await hass.auth.async_create_user(msg["name"]) + user = await hass.auth.async_create_user(msg["name"], msg.get("group_ids")) connection.send_message( websocket_api.result_message(msg["id"], {"user": _user_info(user)}) diff --git a/homeassistant/components/onboarding/views.py b/homeassistant/components/onboarding/views.py index 8eac430ac496..fa859861fb72 100644 --- a/homeassistant/components/onboarding/views.py +++ b/homeassistant/components/onboarding/views.py @@ -3,6 +3,7 @@ import asyncio import voluptuous as vol +from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.components.http.view import HomeAssistantView from homeassistant.core import callback @@ -99,7 +100,7 @@ class UserOnboardingView(_BaseOnboardingView): provider = _async_get_hass_provider(hass) await provider.async_initialize() - user = await hass.auth.async_create_user(data["name"]) + user = await hass.auth.async_create_user(data["name"], [GROUP_ID_ADMIN]) await hass.async_add_executor_job( provider.data.add_auth, data["username"], data["password"] ) diff --git a/tests/auth/test_init.py b/tests/auth/test_init.py index 82c0c0dbdbd4..edcd01d51e1f 100644 --- a/tests/auth/test_init.py +++ b/tests/auth/test_init.py @@ -899,8 +899,8 @@ async def test_async_remove_user(hass): assert events[0].data["user_id"] == user.id -async def test_new_users_admin(mock_hass): - """Test newly created users are admin.""" +async def test_new_users(mock_hass): + """Test newly created users.""" manager = await auth.auth_manager_from_config( mock_hass, [ @@ -911,7 +911,17 @@ async def test_new_users_admin(mock_hass): "username": "test-user", "password": "test-pass", "name": "Test Name", - } + }, + { + "username": "test-user-2", + "password": "test-pass", + "name": "Test Name", + }, + { + "username": "test-user-3", + "password": "test-pass", + "name": "Test Name", + }, ], } ], @@ -920,7 +930,18 @@ async def test_new_users_admin(mock_hass): ensure_auth_manager_loaded(manager) user = await manager.async_create_user("Hello") + # first user in the system is owner and admin + assert user.is_owner assert user.is_admin + assert user.groups == [] + + user = await manager.async_create_user("Hello 2") + assert not user.is_admin + assert user.groups == [] + + user = await manager.async_create_user("Hello 3", ["system-admin"]) + assert user.is_admin + assert user.groups[0].id == "system-admin" user_cred = await manager.async_get_or_create_user( auth_models.Credentials( diff --git a/tests/components/config/test_auth.py b/tests/components/config/test_auth.py index b07df39a8fee..53defb4cd6e2 100644 --- a/tests/components/config/test_auth.py +++ b/tests/components/config/test_auth.py @@ -156,8 +156,35 @@ async def test_create(hass, hass_ws_client, hass_access_token): assert len(await hass.auth.async_get_users()) == 1 + await client.send_json({"id": 5, "type": "config/auth/create", "name": "Paulus"}) + + result = await client.receive_json() + assert result["success"], result + assert len(await hass.auth.async_get_users()) == 2 + data_user = result["result"]["user"] + user = await hass.auth.async_get_user(data_user["id"]) + assert user is not None + assert user.name == data_user["name"] + assert user.is_active + assert user.groups == [] + assert not user.is_admin + assert not user.is_owner + assert not user.system_generated + + +async def test_create_user_group(hass, hass_ws_client, hass_access_token): + """Test create user with a group.""" + client = await hass_ws_client(hass, hass_access_token) + + assert len(await hass.auth.async_get_users()) == 1 + await client.send_json( - {"id": 5, "type": auth_config.WS_TYPE_CREATE, "name": "Paulus"} + { + "id": 5, + "type": "config/auth/create", + "name": "Paulus", + "group_ids": ["system-admin"], + } ) result = await client.receive_json() @@ -168,6 +195,8 @@ async def test_create(hass, hass_ws_client, hass_access_token): assert user is not None assert user.name == data_user["name"] assert user.is_active + assert user.groups[0].id == "system-admin" + assert user.is_admin assert not user.is_owner assert not user.system_generated @@ -176,7 +205,7 @@ async def test_create_requires_admin(hass, hass_ws_client, hass_read_only_access """Test create command requires an admin.""" client = await hass_ws_client(hass, hass_read_only_access_token) - await client.send_json({"id": 5, "type": auth_config.WS_TYPE_CREATE, "name": "YO"}) + await client.send_json({"id": 5, "type": "config/auth/create", "name": "YO"}) result = await client.receive_json() assert not result["success"], result