-
-
Notifications
You must be signed in to change notification settings - Fork 37.8k
Replace api_password in Camera.Push #16339
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
351fb72
c57fab9
8eda035
42f370a
ef507fc
413a93a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,8 +13,10 @@ | |
| from homeassistant.components.camera import Camera, PLATFORM_SCHEMA,\ | ||
| STATE_IDLE, STATE_RECORDING | ||
| from homeassistant.core import callback | ||
| from homeassistant.components.http.view import HomeAssistantView | ||
| from homeassistant.const import CONF_NAME, CONF_TIMEOUT, HTTP_BAD_REQUEST | ||
| from homeassistant.components.http.view import KEY_AUTHENTICATED,\ | ||
| HomeAssistantView | ||
| from homeassistant.const import CONF_NAME, CONF_TIMEOUT,\ | ||
| HTTP_NOT_FOUND, HTTP_UNAUTHORIZED, HTTP_BAD_REQUEST | ||
| from homeassistant.helpers import config_validation as cv | ||
| from homeassistant.helpers.event import async_track_point_in_utc_time | ||
| import homeassistant.util.dt as dt_util | ||
|
|
@@ -25,11 +27,13 @@ | |
|
|
||
| CONF_BUFFER_SIZE = 'buffer' | ||
| CONF_IMAGE_FIELD = 'field' | ||
| CONF_TOKEN = 'token' | ||
|
|
||
| DEFAULT_NAME = "Push Camera" | ||
|
|
||
| ATTR_FILENAME = 'filename' | ||
| ATTR_LAST_TRIP = 'last_trip' | ||
| ATTR_TOKEN = 'token' | ||
|
|
||
| PUSH_CAMERA_DATA = 'push_camera' | ||
|
|
||
|
|
@@ -39,6 +43,7 @@ | |
| vol.Optional(CONF_TIMEOUT, default=timedelta(seconds=5)): vol.All( | ||
| cv.time_period, cv.positive_timedelta), | ||
| vol.Optional(CONF_IMAGE_FIELD, default='image'): cv.string, | ||
| vol.Optional(CONF_TOKEN): vol.All(cv.string, vol.Length(min=8)), | ||
| }) | ||
|
|
||
|
|
||
|
|
@@ -50,7 +55,8 @@ async def async_setup_platform(hass, config, async_add_entities, | |
|
|
||
| cameras = [PushCamera(config[CONF_NAME], | ||
| config[CONF_BUFFER_SIZE], | ||
| config[CONF_TIMEOUT])] | ||
| config[CONF_TIMEOUT], | ||
| config[CONF_TOKEN])] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This expects the CONF_TOKEN to be always set, which is not the case as it can be optional. |
||
|
|
||
| hass.http.register_view(CameraPushReceiver(hass, | ||
| config[CONF_IMAGE_FIELD])) | ||
|
|
@@ -63,6 +69,7 @@ class CameraPushReceiver(HomeAssistantView): | |
|
|
||
| url = "/api/camera_push/{entity_id}" | ||
| name = 'api:camera_push:camera_entity' | ||
| requires_auth = False | ||
|
|
||
| def __init__(self, hass, image_field): | ||
| """Initialize CameraPushReceiver with camera entity.""" | ||
|
|
@@ -75,8 +82,18 @@ async def post(self, request, entity_id): | |
|
|
||
| if _camera is None: | ||
| _LOGGER.error("Unknown %s", entity_id) | ||
| status = HTTP_NOT_FOUND if request[KEY_AUTHENTICATED]\ | ||
| else HTTP_UNAUTHORIZED | ||
| return self.json_message('Unknown {}'.format(entity_id), | ||
| HTTP_BAD_REQUEST) | ||
| status) | ||
|
dgomes marked this conversation as resolved.
|
||
|
|
||
| authenticated = (request[KEY_AUTHENTICATED] or | ||
| request.query.get('token') == _camera.token) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not setting a token will result in token being |
||
|
|
||
| if not authenticated: | ||
| return self.json_message( | ||
| 'Invalid authorization credentials for {}'.format(entity_id), | ||
| HTTP_UNAUTHORIZED) | ||
|
|
||
| try: | ||
| data = await request.post() | ||
|
|
@@ -95,7 +112,7 @@ async def post(self, request, entity_id): | |
| class PushCamera(Camera): | ||
| """The representation of a Push camera.""" | ||
|
|
||
| def __init__(self, name, buffer_size, timeout): | ||
| def __init__(self, name, buffer_size, timeout, token): | ||
| """Initialize push camera component.""" | ||
| super().__init__() | ||
| self._name = name | ||
|
|
@@ -106,6 +123,7 @@ def __init__(self, name, buffer_size, timeout): | |
| self._timeout = timeout | ||
| self.queue = deque([], buffer_size) | ||
| self._current_image = None | ||
| self.token = token | ||
|
|
||
| async def async_added_to_hass(self): | ||
| """Call when entity is added to hass.""" | ||
|
|
@@ -168,5 +186,6 @@ def device_state_attributes(self): | |
| name: value for name, value in ( | ||
| (ATTR_LAST_TRIP, self._last_trip), | ||
| (ATTR_FILENAME, self._filename), | ||
| (ATTR_TOKEN, self.token), | ||
| ) if value is not None | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,6 @@ | |
| from homeassistant import core as ha | ||
| from homeassistant.setup import async_setup_component | ||
| from homeassistant.util import dt as dt_util | ||
| from tests.components.auth import async_setup_auth | ||
|
|
||
|
|
||
| async def test_bad_posting(aioclient_mock, hass, aiohttp_client): | ||
|
|
@@ -15,19 +14,25 @@ async def test_bad_posting(aioclient_mock, hass, aiohttp_client): | |
| 'camera': { | ||
| 'platform': 'push', | ||
| 'name': 'config_test', | ||
| 'token': '12345678' | ||
| }}) | ||
|
|
||
| client = await async_setup_auth(hass, aiohttp_client) | ||
| client = await aiohttp_client(hass.http.app) | ||
|
|
||
| # missing file | ||
| resp = await client.post('/api/camera_push/camera.config_test') | ||
| assert resp.status == 400 | ||
|
|
||
| files = {'image': io.BytesIO(b'fake')} | ||
|
|
||
| # wrong entity | ||
| files = {'image': io.BytesIO(b'fake')} | ||
| resp = await client.post('/api/camera_push/camera.wrong', data=files) | ||
| assert resp.status == 400 | ||
| assert resp.status == 404 | ||
|
|
||
| # wrong token but authenticated user | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did I missed something? How this be authenticated user? I didn't find the code to set Authorization header.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. client always makes authenticated requests... how can I disable it ?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| files = {'image': io.BytesIO(b'fake')} | ||
| resp = await client.post('/api/camera_push/camera.config_test?token=1234', | ||
| data=files) | ||
| assert resp.status == 200 | ||
|
|
||
|
|
||
| async def test_posting_url(hass, aiohttp_client): | ||
|
|
@@ -36,6 +41,7 @@ async def test_posting_url(hass, aiohttp_client): | |
| 'camera': { | ||
| 'platform': 'push', | ||
| 'name': 'config_test', | ||
| 'token': '12345678' | ||
| }}) | ||
|
|
||
| client = await aiohttp_client(hass.http.app) | ||
|
|
@@ -46,7 +52,9 @@ async def test_posting_url(hass, aiohttp_client): | |
| assert camera_state.state == 'idle' | ||
|
|
||
| # post image | ||
| resp = await client.post('/api/camera_push/camera.config_test', data=files) | ||
| resp = await client.post( | ||
| '/api/camera_push/camera.config_test?token=12345678', | ||
| data=files) | ||
| assert resp.status == 200 | ||
|
|
||
| # state recording | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be required
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made it optional, since the implementation allows the use of the normal authentication.