diff --git a/homeassistant/components/cloud/auth_api.py b/homeassistant/components/cloud/auth_api.py index 9cad3ec77f3..0ca0451e565 100644 --- a/homeassistant/components/cloud/auth_api.py +++ b/homeassistant/components/cloud/auth_api.py @@ -95,6 +95,21 @@ def confirm_register(cloud, confirmation_code, email): raise _map_aws_exception(err) +def resend_email_confirm(cloud, email): + """Resend email confirmation.""" + from botocore.exceptions import ClientError + + cognito = _cognito(cloud, username=email) + + try: + cognito.client.resend_confirmation_code( + Username=email, + ClientId=cognito.client_id + ) + except ClientError as err: + raise _map_aws_exception(err) + + def forgot_password(cloud, email): """Initiate forgotten password flow.""" from botocore.exceptions import ClientError diff --git a/homeassistant/components/cloud/http_api.py b/homeassistant/components/cloud/http_api.py index 27fd6f604c0..25873ba158c 100644 --- a/homeassistant/components/cloud/http_api.py +++ b/homeassistant/components/cloud/http_api.py @@ -23,6 +23,7 @@ def async_setup(hass): hass.http.register_view(CloudAccountView) hass.http.register_view(CloudRegisterView) hass.http.register_view(CloudConfirmRegisterView) + hass.http.register_view(CloudResendConfirmView) hass.http.register_view(CloudForgotPasswordView) hass.http.register_view(CloudConfirmForgotPasswordView) @@ -172,6 +173,29 @@ class CloudConfirmRegisterView(HomeAssistantView): return self.json_message('ok') +class CloudResendConfirmView(HomeAssistantView): + """Resend email confirmation code.""" + + url = '/api/cloud/resend_confirm' + name = 'api:cloud:resend_confirm' + + @_handle_cloud_errors + @RequestDataValidator(vol.Schema({ + vol.Required('email'): str, + })) + @asyncio.coroutine + def post(self, request, data): + """Handle resending confirm email code request.""" + hass = request.app['hass'] + cloud = hass.data[DOMAIN] + + with async_timeout.timeout(REQUEST_TIMEOUT, loop=hass.loop): + yield from hass.async_add_job( + auth_api.resend_email_confirm, cloud, data['email']) + + return self.json_message('ok') + + class CloudForgotPasswordView(HomeAssistantView): """View to start Forgot Password flow..""" diff --git a/tests/components/cloud/test_auth_api.py b/tests/components/cloud/test_auth_api.py index f94c2691cd7..bb28dfc50e3 100644 --- a/tests/components/cloud/test_auth_api.py +++ b/tests/components/cloud/test_auth_api.py @@ -119,6 +119,22 @@ def test_confirm_register_fails(mock_cognito): auth_api.confirm_register(cloud, '123456', 'email@home-assistant.io') +def test_resend_email_confirm(mock_cognito): + """Test starting forgot password flow.""" + cloud = MagicMock() + auth_api.resend_email_confirm(cloud, 'email@home-assistant.io') + assert len(mock_cognito.client.resend_confirmation_code.mock_calls) == 1 + + +def test_resend_email_confirm_fails(mock_cognito): + """Test failure when starting forgot password flow.""" + cloud = MagicMock() + mock_cognito.client.resend_confirmation_code.side_effect = \ + aws_error('SomeError') + with pytest.raises(auth_api.CloudError): + auth_api.resend_email_confirm(cloud, 'email@home-assistant.io') + + def test_forgot_password(mock_cognito): """Test starting forgot password flow.""" cloud = MagicMock() diff --git a/tests/components/cloud/test_http_api.py b/tests/components/cloud/test_http_api.py index 423ca1092eb..2c71f504c50 100644 --- a/tests/components/cloud/test_http_api.py +++ b/tests/components/cloud/test_http_api.py @@ -315,6 +315,48 @@ def test_forgot_password_view_unknown_error(mock_cognito, cloud_client): assert req.status == 502 +@asyncio.coroutine +def test_resend_confirm_view(mock_cognito, cloud_client): + """Test logging out.""" + req = yield from cloud_client.post('/api/cloud/resend_confirm', json={ + 'email': 'hello@bla.com', + }) + assert req.status == 200 + assert len(mock_cognito.client.resend_confirmation_code.mock_calls) == 1 + + +@asyncio.coroutine +def test_resend_confirm_view_bad_data(mock_cognito, cloud_client): + """Test logging out.""" + req = yield from cloud_client.post('/api/cloud/resend_confirm', json={ + 'not_email': 'hello@bla.com', + }) + assert req.status == 400 + assert len(mock_cognito.client.resend_confirmation_code.mock_calls) == 0 + + +@asyncio.coroutine +def test_resend_confirm_view_request_timeout(mock_cognito, cloud_client): + """Test timeout while logging out.""" + mock_cognito.client.resend_confirmation_code.side_effect = \ + asyncio.TimeoutError + req = yield from cloud_client.post('/api/cloud/resend_confirm', json={ + 'email': 'hello@bla.com', + }) + assert req.status == 502 + + +@asyncio.coroutine +def test_resend_confirm_view_unknown_error(mock_cognito, cloud_client): + """Test unknown error while logging out.""" + mock_cognito.client.resend_confirmation_code.side_effect = \ + auth_api.UnknownError + req = yield from cloud_client.post('/api/cloud/resend_confirm', json={ + 'email': 'hello@bla.com', + }) + assert req.status == 502 + + @asyncio.coroutine def test_confirm_forgot_password_view(mock_cognito, cloud_client): """Test logging out."""