From 61bf4d8a29d359f8e4066c760d01beb69a504b54 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 11 Oct 2018 17:06:51 +0200 Subject: [PATCH] Add user events (#17328) --- homeassistant/auth/__init__.py | 31 +++++++++++++-- tests/auth/test_init.py | 72 ++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/homeassistant/auth/__init__.py b/homeassistant/auth/__init__.py index e19498026d1..e584d5b70e5 100644 --- a/homeassistant/auth/__init__.py +++ b/homeassistant/auth/__init__.py @@ -16,6 +16,9 @@ from . import auth_store, models from .mfa_modules import auth_mfa_module_from_config, MultiFactorAuthModule from .providers import auth_provider_from_config, AuthProvider, LoginFlow +EVENT_USER_ADDED = 'user_added' +EVENT_USER_REMOVED = 'user_removed' + _LOGGER = logging.getLogger(__name__) _MfaModuleDict = Dict[str, MultiFactorAuthModule] _ProviderKey = Tuple[str, Optional[str]] @@ -126,13 +129,19 @@ class AuthManager: async def async_create_system_user(self, name: str) -> models.User: """Create a system user.""" - return await self._store.async_create_user( + user = await self._store.async_create_user( name=name, system_generated=True, is_active=True, groups=[], ) + self.hass.bus.async_fire(EVENT_USER_ADDED, { + 'user_id': user.id + }) + + return user + async def async_create_user(self, name: str) -> models.User: """Create a user.""" group = (await self._store.async_get_groups())[0] @@ -145,7 +154,13 @@ class AuthManager: if await self._user_should_be_owner(): kwargs['is_owner'] = True - return await self._store.async_create_user(**kwargs) + user = await self._store.async_create_user(**kwargs) + + self.hass.bus.async_fire(EVENT_USER_ADDED, { + 'user_id': user.id + }) + + return user async def async_get_or_create_user(self, credentials: models.Credentials) \ -> models.User: @@ -165,12 +180,18 @@ class AuthManager: info = await auth_provider.async_user_meta_for_credentials( credentials) - return await self._store.async_create_user( + user = await self._store.async_create_user( credentials=credentials, name=info.name, is_active=info.is_active, ) + self.hass.bus.async_fire(EVENT_USER_ADDED, { + 'user_id': user.id + }) + + return user + async def async_link_user(self, user: models.User, credentials: models.Credentials) -> None: """Link credentials to an existing user.""" @@ -188,6 +209,10 @@ class AuthManager: await self._store.async_remove_user(user) + self.hass.bus.async_fire(EVENT_USER_REMOVED, { + 'user_id': user.id + }) + async def async_activate_user(self, user: models.User) -> None: """Activate a user.""" await self._store.async_activate_user(user) diff --git a/tests/auth/test_init.py b/tests/auth/test_init.py index 8fd9b8930e4..f6086db7516 100644 --- a/tests/auth/test_init.py +++ b/tests/auth/test_init.py @@ -10,6 +10,7 @@ from homeassistant import auth, data_entry_flow from homeassistant.auth import ( models as auth_models, auth_store, const as auth_const) from homeassistant.auth.const import MFA_SESSION_EXPIRATION +from homeassistant.core import callback from homeassistant.util import dt as dt_util from tests.common import ( MockUser, ensure_auth_manager_loaded, flush_store, CLIENT_ID) @@ -138,6 +139,14 @@ async def test_auth_manager_from_config_auth_modules(mock_hass): async def test_create_new_user(hass): """Test creating new user.""" + events = [] + + @callback + def user_added(event): + events.append(event) + + hass.bus.async_listen('user_added', user_added) + manager = await auth.auth_manager_from_config(hass, [{ 'type': 'insecure_example', 'users': [{ @@ -160,6 +169,10 @@ async def test_create_new_user(hass): assert user.is_owner is False assert user.name == 'Test Name' + await hass.async_block_till_done() + assert len(events) == 1 + assert events[0].data['user_id'] == user.id + async def test_login_as_existing_user(mock_hass): """Test login as existing user.""" @@ -331,6 +344,14 @@ async def test_cannot_retrieve_expired_access_token(hass): async def test_generating_system_user(hass): """Test that we can add a system user.""" + events = [] + + @callback + def user_added(event): + events.append(event) + + hass.bus.async_listen('user_added', user_added) + manager = await auth.auth_manager_from_config(hass, [], []) user = await manager.async_create_system_user('Hass.io') token = await manager.async_create_refresh_token(user) @@ -338,6 +359,10 @@ async def test_generating_system_user(hass): assert token is not None assert token.client_id is None + await hass.async_block_till_done() + assert len(events) == 1 + assert events[0].data['user_id'] == user.id + async def test_refresh_token_requires_client_for_user(hass): """Test create refresh token for a user with client_id.""" @@ -797,3 +822,50 @@ async def test_enable_mfa_for_user(hass, hass_storage): # disable mfa for user don't enabled just silent fail await manager.async_disable_user_mfa(user, 'insecure_example') + + +async def test_async_remove_user(hass): + """Test removing a user.""" + events = [] + + @callback + def user_removed(event): + events.append(event) + + hass.bus.async_listen('user_removed', user_removed) + + manager = await auth.auth_manager_from_config(hass, [{ + 'type': 'insecure_example', + 'users': [{ + 'username': 'test-user', + 'password': 'test-pass', + 'name': 'Test Name' + }] + }], []) + hass.auth = manager + ensure_auth_manager_loaded(manager) + + # Add fake user with credentials for example auth provider. + user = MockUser( + id='mock-user', + is_owner=False, + is_active=False, + name='Paulus', + ).add_to_auth_manager(manager) + user.credentials.append(auth_models.Credentials( + id='mock-id', + auth_provider_type='insecure_example', + auth_provider_id=None, + data={'username': 'test-user'}, + is_new=False, + )) + assert len(user.credentials) == 1 + + await hass.auth.async_remove_user(user) + + assert len(await manager.async_get_users()) == 0 + assert len(user.credentials) == 0 + + await hass.async_block_till_done() + assert len(events) == 1 + assert events[0].data['user_id'] == user.id