From 57a00c1fbffd04a9c90af14586d168e8242ed2a5 Mon Sep 17 00:00:00 2001 From: Klaas Hoekema <khoekema@azavea.com> Date: Tue, 4 Apr 2017 04:55:43 -0400 Subject: [PATCH] Allow token authentication for 'hook' switch component (#6922) The Hook switch component currently uses username/password to get a token from the Hook web app to use for subsequent requests. This adds an option to specify the token directly in config. Makes the 'password' and 'token' options mutually exclusive since otherwise one would have to take precedence, and it seems worth preventing people from filling their config with passwords that don't even need to be there. --- homeassistant/components/switch/hook.py | 48 ++++++++++++++----------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/switch/hook.py b/homeassistant/components/switch/hook.py index 33b49a24caa..58d3813f6fa 100644 --- a/homeassistant/components/switch/hook.py +++ b/homeassistant/components/switch/hook.py @@ -12,7 +12,7 @@ import async_timeout import aiohttp from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) -from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, CONF_TOKEN from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv @@ -22,8 +22,12 @@ HOOK_ENDPOINT = 'https://api.gethook.io/v1/' TIMEOUT = 10 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, + vol.Exclusive(CONF_PASSWORD, 'hook_secret', msg='hook: provide ' + + 'username/password OR token'): cv.string, + vol.Exclusive(CONF_TOKEN, 'hook_secret', msg='hook: provide ' + + 'username/password OR token'): cv.string, + vol.Inclusive(CONF_USERNAME, 'hook_auth'): cv.string, + vol.Inclusive(CONF_PASSWORD, 'hook_auth'): cv.string, }) @@ -32,31 +36,33 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): """Set up Hook by getting the access token and list of actions.""" username = config.get(CONF_USERNAME) password = config.get(CONF_PASSWORD) + token = config.get(CONF_TOKEN) websession = async_get_clientsession(hass) + # If password is set in config, prefer it over token + if username is not None and password is not None: + try: + with async_timeout.timeout(TIMEOUT, loop=hass.loop): + response = yield from websession.post( + '{}{}'.format(HOOK_ENDPOINT, 'user/login'), + data={ + 'username': username, + 'password': password}) + data = yield from response.json() + except (asyncio.TimeoutError, aiohttp.ClientError) as error: + _LOGGER.error("Failed authentication API call: %s", error) + return False - try: - with async_timeout.timeout(TIMEOUT, loop=hass.loop): - response = yield from websession.post( - '{}{}'.format(HOOK_ENDPOINT, 'user/login'), - data={ - 'username': username, - 'password': password}) - data = yield from response.json() - except (asyncio.TimeoutError, aiohttp.ClientError) as error: - _LOGGER.error("Failed authentication API call: %s", error) - return False - - try: - token = data['data']['token'] - except KeyError: - _LOGGER.error("No token. Check username and password") - return False + try: + token = data['data']['token'] + except KeyError: + _LOGGER.error("No token. Check username and password") + return False try: with async_timeout.timeout(TIMEOUT, loop=hass.loop): response = yield from websession.get( '{}{}'.format(HOOK_ENDPOINT, 'device'), - params={"token": data['data']['token']}) + params={"token": token}) data = yield from response.json() except (asyncio.TimeoutError, aiohttp.ClientError) as error: _LOGGER.error("Failed getting devices: %s", error) -- GitLab