Rename is_owner decorator to is_admin (#19266)

* Rename is_owner decorator to is_admin

* Update test_auth.py
This commit is contained in:
Paulus Schoutsen 2018-12-14 10:19:27 +01:00 committed by GitHub
parent a5a896b519
commit 4f98818258
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 42 additions and 61 deletions

View File

@ -39,7 +39,7 @@ async def async_setup(hass):
return True return True
@websocket_api.require_owner @websocket_api.require_admin
@websocket_api.async_response @websocket_api.async_response
async def websocket_list(hass, connection, msg): async def websocket_list(hass, connection, msg):
"""Return a list of users.""" """Return a list of users."""
@ -49,7 +49,7 @@ async def websocket_list(hass, connection, msg):
websocket_api.result_message(msg['id'], result)) websocket_api.result_message(msg['id'], result))
@websocket_api.require_owner @websocket_api.require_admin
@websocket_api.async_response @websocket_api.async_response
async def websocket_delete(hass, connection, msg): async def websocket_delete(hass, connection, msg):
"""Delete a user.""" """Delete a user."""
@ -72,7 +72,7 @@ async def websocket_delete(hass, connection, msg):
websocket_api.result_message(msg['id'])) websocket_api.result_message(msg['id']))
@websocket_api.require_owner @websocket_api.require_admin
@websocket_api.async_response @websocket_api.async_response
async def websocket_create(hass, connection, msg): async def websocket_create(hass, connection, msg):
"""Create a user.""" """Create a user."""

View File

@ -3,7 +3,6 @@ import voluptuous as vol
from homeassistant.auth.providers import homeassistant as auth_ha from homeassistant.auth.providers import homeassistant as auth_ha
from homeassistant.components import websocket_api from homeassistant.components import websocket_api
from homeassistant.components.websocket_api.decorators import require_owner
WS_TYPE_CREATE = 'config/auth_provider/homeassistant/create' WS_TYPE_CREATE = 'config/auth_provider/homeassistant/create'
@ -54,7 +53,7 @@ def _get_provider(hass):
raise RuntimeError('Provider not found') raise RuntimeError('Provider not found')
@require_owner @websocket_api.require_admin
@websocket_api.async_response @websocket_api.async_response
async def websocket_create(hass, connection, msg): async def websocket_create(hass, connection, msg):
"""Create credentials and attach to a user.""" """Create credentials and attach to a user."""
@ -91,7 +90,7 @@ async def websocket_create(hass, connection, msg):
connection.send_message(websocket_api.result_message(msg['id'])) connection.send_message(websocket_api.result_message(msg['id']))
@require_owner @websocket_api.require_admin
@websocket_api.async_response @websocket_api.async_response
async def websocket_delete(hass, connection, msg): async def websocket_delete(hass, connection, msg):
"""Delete username and related credential.""" """Delete username and related credential."""
@ -123,6 +122,7 @@ async def websocket_delete(hass, connection, msg):
websocket_api.result_message(msg['id'])) websocket_api.result_message(msg['id']))
@websocket_api.require_admin
@websocket_api.async_response @websocket_api.async_response
async def websocket_change_password(hass, connection, msg): async def websocket_change_password(hass, connection, msg):
"""Change user password.""" """Change user password."""

View File

@ -20,7 +20,7 @@ BASE_COMMAND_MESSAGE_SCHEMA = messages.BASE_COMMAND_MESSAGE_SCHEMA
error_message = messages.error_message error_message = messages.error_message
result_message = messages.result_message result_message = messages.result_message
async_response = decorators.async_response async_response = decorators.async_response
require_owner = decorators.require_owner require_admin = decorators.require_admin
ws_require_user = decorators.ws_require_user ws_require_user = decorators.ws_require_user
# pylint: enable=invalid-name # pylint: enable=invalid-name

View File

@ -3,6 +3,7 @@ from functools import wraps
import logging import logging
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.exceptions import Unauthorized
from . import messages from . import messages
@ -30,21 +31,19 @@ def async_response(func):
return schedule_handler return schedule_handler
def require_owner(func): def require_admin(func):
"""Websocket decorator to require user to be an owner.""" """Websocket decorator to require user to be an admin."""
@wraps(func) @wraps(func)
def with_owner(hass, connection, msg): def with_admin(hass, connection, msg):
"""Check owner and call function.""" """Check admin and call function."""
user = connection.user user = connection.user
if user is None or not user.is_owner: if user is None or not user.is_admin:
connection.send_message(messages.error_message( raise Unauthorized()
msg['id'], 'unauthorized', 'This command is for owners only.'))
return
func(hass, connection, msg) func(hass, connection, msg)
return with_owner return with_admin
def ws_require_user( def ws_require_user(

View File

@ -13,9 +13,10 @@ def setup_config(hass, aiohttp_client):
hass.loop.run_until_complete(auth_config.async_setup(hass)) hass.loop.run_until_complete(auth_config.async_setup(hass))
async def test_list_requires_owner(hass, hass_ws_client, hass_access_token): async def test_list_requires_admin(hass, hass_ws_client,
hass_read_only_access_token):
"""Test get users requires auth.""" """Test get users requires auth."""
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_read_only_access_token)
await client.send_json({ await client.send_json({
'id': 5, 'id': 5,
@ -109,9 +110,10 @@ async def test_list(hass, hass_ws_client, hass_admin_user):
} }
async def test_delete_requires_owner(hass, hass_ws_client, hass_access_token): async def test_delete_requires_admin(hass, hass_ws_client,
hass_read_only_access_token):
"""Test delete command requires an owner.""" """Test delete command requires an owner."""
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_read_only_access_token)
await client.send_json({ await client.send_json({
'id': 5, 'id': 5,
@ -139,15 +141,12 @@ async def test_delete_unable_self_account(hass, hass_ws_client,
result = await client.receive_json() result = await client.receive_json()
assert not result['success'], result assert not result['success'], result
assert result['error']['code'] == 'unauthorized' assert result['error']['code'] == 'no_delete_self'
async def test_delete_unknown_user(hass, hass_ws_client, hass_access_token): async def test_delete_unknown_user(hass, hass_ws_client, hass_access_token):
"""Test we cannot delete an unknown user.""" """Test we cannot delete an unknown user."""
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_access_token)
refresh_token = await hass.auth.async_validate_access_token(
hass_access_token)
refresh_token.user.is_owner = True
await client.send_json({ await client.send_json({
'id': 5, 'id': 5,
@ -163,9 +162,6 @@ async def test_delete_unknown_user(hass, hass_ws_client, hass_access_token):
async def test_delete(hass, hass_ws_client, hass_access_token): async def test_delete(hass, hass_ws_client, hass_access_token):
"""Test delete command works.""" """Test delete command works."""
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_access_token)
refresh_token = await hass.auth.async_validate_access_token(
hass_access_token)
refresh_token.user.is_owner = True
test_user = MockUser( test_user = MockUser(
id='efg', id='efg',
).add_to_hass(hass) ).add_to_hass(hass)
@ -186,9 +182,6 @@ async def test_delete(hass, hass_ws_client, hass_access_token):
async def test_create(hass, hass_ws_client, hass_access_token): async def test_create(hass, hass_ws_client, hass_access_token):
"""Test create command works.""" """Test create command works."""
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_access_token)
refresh_token = await hass.auth.async_validate_access_token(
hass_access_token)
refresh_token.user.is_owner = True
assert len(await hass.auth.async_get_users()) == 1 assert len(await hass.auth.async_get_users()) == 1
@ -210,9 +203,10 @@ async def test_create(hass, hass_ws_client, hass_access_token):
assert not user.system_generated assert not user.system_generated
async def test_create_requires_owner(hass, hass_ws_client, hass_access_token): async def test_create_requires_admin(hass, hass_ws_client,
hass_read_only_access_token):
"""Test create command requires an owner.""" """Test create command requires an owner."""
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_read_only_access_token)
await client.send_json({ await client.send_json({
'id': 5, 'id': 5,

View File

@ -22,9 +22,6 @@ async def test_create_auth_system_generated_user(hass, hass_access_token,
"""Test we can't add auth to system generated users.""" """Test we can't add auth to system generated users."""
system_user = MockUser(system_generated=True).add_to_hass(hass) system_user = MockUser(system_generated=True).add_to_hass(hass)
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_access_token)
refresh_token = await hass.auth.async_validate_access_token(
hass_access_token)
refresh_token.user.is_owner = True
await client.send_json({ await client.send_json({
'id': 5, 'id': 5,
@ -49,9 +46,6 @@ async def test_create_auth_unknown_user(hass_ws_client, hass,
hass_access_token): hass_access_token):
"""Test create pointing at unknown user.""" """Test create pointing at unknown user."""
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_access_token)
refresh_token = await hass.auth.async_validate_access_token(
hass_access_token)
refresh_token.user.is_owner = True
await client.send_json({ await client.send_json({
'id': 5, 'id': 5,
@ -67,10 +61,10 @@ async def test_create_auth_unknown_user(hass_ws_client, hass,
assert result['error']['code'] == 'not_found' assert result['error']['code'] == 'not_found'
async def test_create_auth_requires_owner(hass, hass_ws_client, async def test_create_auth_requires_admin(hass, hass_ws_client,
hass_access_token): hass_read_only_access_token):
"""Test create requires owner to call API.""" """Test create requires admin to call API."""
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_read_only_access_token)
await client.send_json({ await client.send_json({
'id': 5, 'id': 5,
@ -90,9 +84,6 @@ async def test_create_auth(hass, hass_ws_client, hass_access_token,
"""Test create auth command works.""" """Test create auth command works."""
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_access_token)
user = MockUser().add_to_hass(hass) user = MockUser().add_to_hass(hass)
refresh_token = await hass.auth.async_validate_access_token(
hass_access_token)
refresh_token.user.is_owner = True
assert len(user.credentials) == 0 assert len(user.credentials) == 0
@ -123,9 +114,6 @@ async def test_create_auth_duplicate_username(hass, hass_ws_client,
"""Test we can't create auth with a duplicate username.""" """Test we can't create auth with a duplicate username."""
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_access_token)
user = MockUser().add_to_hass(hass) user = MockUser().add_to_hass(hass)
refresh_token = await hass.auth.async_validate_access_token(
hass_access_token)
refresh_token.user.is_owner = True
hass_storage[prov_ha.STORAGE_KEY] = { hass_storage[prov_ha.STORAGE_KEY] = {
'version': 1, 'version': 1,
@ -153,9 +141,6 @@ async def test_delete_removes_just_auth(hass_ws_client, hass, hass_storage,
hass_access_token): hass_access_token):
"""Test deleting an auth without being connected to a user.""" """Test deleting an auth without being connected to a user."""
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_access_token)
refresh_token = await hass.auth.async_validate_access_token(
hass_access_token)
refresh_token.user.is_owner = True
hass_storage[prov_ha.STORAGE_KEY] = { hass_storage[prov_ha.STORAGE_KEY] = {
'version': 1, 'version': 1,
@ -181,9 +166,6 @@ async def test_delete_removes_credential(hass, hass_ws_client,
hass_access_token, hass_storage): hass_access_token, hass_storage):
"""Test deleting auth that is connected to a user.""" """Test deleting auth that is connected to a user."""
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_access_token)
refresh_token = await hass.auth.async_validate_access_token(
hass_access_token)
refresh_token.user.is_owner = True
user = MockUser().add_to_hass(hass) user = MockUser().add_to_hass(hass)
user.credentials.append( user.credentials.append(
@ -210,9 +192,10 @@ async def test_delete_removes_credential(hass, hass_ws_client,
assert len(hass_storage[prov_ha.STORAGE_KEY]['data']['users']) == 0 assert len(hass_storage[prov_ha.STORAGE_KEY]['data']['users']) == 0
async def test_delete_requires_owner(hass, hass_ws_client, hass_access_token): async def test_delete_requires_admin(hass, hass_ws_client,
"""Test delete requires owner.""" hass_read_only_access_token):
client = await hass_ws_client(hass, hass_access_token) """Test delete requires admin."""
client = await hass_ws_client(hass, hass_read_only_access_token)
await client.send_json({ await client.send_json({
'id': 5, 'id': 5,
@ -228,9 +211,6 @@ async def test_delete_requires_owner(hass, hass_ws_client, hass_access_token):
async def test_delete_unknown_auth(hass, hass_ws_client, hass_access_token): async def test_delete_unknown_auth(hass, hass_ws_client, hass_access_token):
"""Test trying to delete an unknown auth username.""" """Test trying to delete an unknown auth username."""
client = await hass_ws_client(hass, hass_access_token) client = await hass_ws_client(hass, hass_access_token)
refresh_token = await hass.auth.async_validate_access_token(
hass_access_token)
refresh_token.user.is_owner = True
await client.send_json({ await client.send_json({
'id': 5, 'id': 5,

View File

@ -142,7 +142,7 @@ def hass_access_token(hass, hass_admin_user):
"""Return an access token to access Home Assistant.""" """Return an access token to access Home Assistant."""
refresh_token = hass.loop.run_until_complete( refresh_token = hass.loop.run_until_complete(
hass.auth.async_create_refresh_token(hass_admin_user, CLIENT_ID)) hass.auth.async_create_refresh_token(hass_admin_user, CLIENT_ID))
yield hass.auth.async_create_access_token(refresh_token) return hass.auth.async_create_access_token(refresh_token)
@pytest.fixture @pytest.fixture
@ -167,6 +167,14 @@ def hass_read_only_user(hass, local_auth):
return MockUser(groups=[read_only_group]).add_to_hass(hass) return MockUser(groups=[read_only_group]).add_to_hass(hass)
@pytest.fixture
def hass_read_only_access_token(hass, hass_read_only_user):
"""Return a Home Assistant read only user."""
refresh_token = hass.loop.run_until_complete(
hass.auth.async_create_refresh_token(hass_read_only_user, CLIENT_ID))
return hass.auth.async_create_access_token(refresh_token)
@pytest.fixture @pytest.fixture
def legacy_auth(hass): def legacy_auth(hass):
"""Load legacy API password provider.""" """Load legacy API password provider."""