mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
Rename is_owner decorator to is_admin (#19266)
* Rename is_owner decorator to is_admin * Update test_auth.py
This commit is contained in:
parent
a5a896b519
commit
4f98818258
@ -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."""
|
||||||
|
@ -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."""
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user