Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 5 additions & 86 deletions homeassistant/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,16 +186,6 @@ class Credentials:
is_new = attr.ib(type=bool, default=True)


@attr.s(slots=True)
class Client:
"""Client that interacts with Home Assistant on behalf of a user."""

name = attr.ib(type=str)
id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex))
secret = attr.ib(type=str, default=attr.Factory(generate_secret))
redirect_uris = attr.ib(type=list, default=attr.Factory(list))


async def load_auth_provider_module(hass, provider):
"""Load an auth provider."""
try:
Expand Down Expand Up @@ -356,20 +346,20 @@ async def async_remove_user(self, user):
"""Remove a user."""
await self._store.async_remove_user(user)

async def async_create_refresh_token(self, user, client=None):
async def async_create_refresh_token(self, user, client_id=None):
"""Create a new refresh token for a user."""
if not user.is_active:
raise ValueError('User is not active')

if user.system_generated and client is not None:
if user.system_generated and client_id is not None:
raise ValueError(
'System generated users cannot have refresh tokens connected '
'to a client.')

if not user.system_generated and client is None:
if not user.system_generated and client_id is None:
raise ValueError('Client is required to generate a refresh token.')

return await self._store.async_create_refresh_token(user, client)
return await self._store.async_create_refresh_token(user, client_id)

async def async_get_refresh_token(self, token):
"""Get refresh token by token."""
Expand All @@ -396,26 +386,6 @@ def async_get_access_token(self, token):

return tkn

async def async_create_client(self, name, *, redirect_uris=None,
no_secret=False):
"""Create a new client."""
return await self._store.async_create_client(
name, redirect_uris, no_secret)

async def async_get_or_create_client(self, name, *, redirect_uris=None,
no_secret=False):
"""Find a client, if not exists, create a new one."""
for client in await self._store.async_get_clients():
if client.name == name:
return client

return await self._store.async_create_client(
name, redirect_uris, no_secret)

async def async_get_client(self, client_id):
"""Get a client."""
return await self._store.async_get_client(client_id)

async def _async_create_login_flow(self, handler, *, source, data):
"""Create a login flow."""
auth_provider = self._providers[handler]
Expand Down Expand Up @@ -456,7 +426,6 @@ def __init__(self, hass):
"""Initialize the auth store."""
self.hass = hass
self._users = None
self._clients = None
self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY)

async def async_get_users(self):
Expand Down Expand Up @@ -515,9 +484,8 @@ async def async_remove_user(self, user):
self._users.pop(user.id)
await self.async_save()

async def async_create_refresh_token(self, user, client=None):
async def async_create_refresh_token(self, user, client_id=None):
"""Create a new token for a user."""
client_id = client.id if client is not None else None
refresh_token = RefreshToken(user=user, client_id=client_id)
user.refresh_tokens[refresh_token.token] = refresh_token
await self.async_save()
Expand All @@ -535,38 +503,6 @@ async def async_get_refresh_token(self, token):

return None

async def async_create_client(self, name, redirect_uris, no_secret):
"""Create a new client."""
if self._clients is None:
await self.async_load()

kwargs = {
'name': name,
'redirect_uris': redirect_uris
}

if no_secret:
kwargs['secret'] = None

client = Client(**kwargs)
self._clients[client.id] = client
await self.async_save()
return client

async def async_get_clients(self):
"""Return all clients."""
if self._clients is None:
await self.async_load()

return list(self._clients.values())

async def async_get_client(self, client_id):
"""Get a client."""
if self._clients is None:
await self.async_load()

return self._clients.get(client_id)

async def async_load(self):
"""Load the users."""
data = await self._store.async_load()
Expand All @@ -578,7 +514,6 @@ async def async_load(self):

if data is None:
self._users = {}
self._clients = {}
return

users = {
Expand Down Expand Up @@ -618,12 +553,7 @@ async def async_load(self):
)
refresh_token.access_tokens.append(token)

clients = {
cl_dict['id']: Client(**cl_dict) for cl_dict in data['clients']
}

self._users = users
self._clients = clients

async def async_save(self):
"""Save users."""
Expand Down Expand Up @@ -676,19 +606,8 @@ async def async_save(self):
for access_token in refresh_token.access_tokens
]

clients = [
{
'id': client.id,
'name': client.name,
'secret': client.secret,
'redirect_uris': client.redirect_uris,
}
for client in self._clients.values()
]

data = {
'users': users,
'clients': clients,
'credentials': credentials,
'access_tokens': access_tokens,
'refresh_tokens': refresh_tokens,
Expand Down
59 changes: 35 additions & 24 deletions homeassistant/components/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.components.http.data_validator import RequestDataValidator

from .client import verify_client
from . import indieauth


DOMAIN = 'auth'
DEPENDENCIES = ['http']
Expand Down Expand Up @@ -143,8 +144,7 @@ class AuthProvidersView(HomeAssistantView):
name = 'api:auth:providers'
requires_auth = False

@verify_client
async def get(self, request, client):
async def get(self, request):
"""Get available auth providers."""
return self.json([{
'name': provider.name,
Expand All @@ -164,16 +164,16 @@ async def get(self, request):
"""Do not allow index of flows in progress."""
return aiohttp.web.Response(status=405)

# pylint: disable=arguments-differ
@verify_client
@RequestDataValidator(vol.Schema({
vol.Required('client_id'): str,
vol.Required('handler'): vol.Any(str, list),
vol.Required('redirect_uri'): str,
}))
async def post(self, request, client, data):
async def post(self, request, data):
"""Create a new login flow."""
if data['redirect_uri'] not in client.redirect_uris:
return self.json_message('invalid redirect uri', )
if not indieauth.verify_redirect_uri(data['client_id'],
data['redirect_uri']):
return self.json_message('invalid client id or redirect uri', 400)

# pylint: disable=no-value-for-parameter
return await super().post(request)
Expand All @@ -191,16 +191,20 @@ def __init__(self, flow_mgr, store_credentials):
super().__init__(flow_mgr)
self._store_credentials = store_credentials

# pylint: disable=arguments-differ
async def get(self, request):
async def get(self, request, flow_id):
"""Do not allow getting status of a flow in progress."""
return self.json_message('Invalid flow specified', 404)

# pylint: disable=arguments-differ
@verify_client
@RequestDataValidator(vol.Schema(dict), allow_empty=True)
async def post(self, request, client, flow_id, data):
@RequestDataValidator(vol.Schema({
'client_id': str
}, extra=vol.ALLOW_EXTRA))
async def post(self, request, flow_id, data):
"""Handle progressing a login flow request."""
client_id = data.pop('client_id')

if not indieauth.verify_client_id(client_id):
return self.json_message('Invalid client id', 400)

try:
result = await self._flow_mgr.async_configure(flow_id, data)
except data_entry_flow.UnknownFlow:
Expand All @@ -212,7 +216,7 @@ async def post(self, request, client, flow_id, data):
return self.json(self._prepare_result_json(result))

result.pop('data')
result['result'] = self._store_credentials(client.id, result['result'])
result['result'] = self._store_credentials(client_id, result['result'])

return self.json(result)

Expand All @@ -228,24 +232,31 @@ def __init__(self, retrieve_credentials):
"""Initialize the grant token view."""
self._retrieve_credentials = retrieve_credentials

@verify_client
async def post(self, request, client):
async def post(self, request):
"""Grant a token."""
hass = request.app['hass']
data = await request.post()

client_id = data.get('client_id')
if client_id is None or not indieauth.verify_client_id(client_id):
return self.json({
'error': 'invalid_request',
}, status_code=400)

grant_type = data.get('grant_type')

if grant_type == 'authorization_code':
return await self._async_handle_auth_code(hass, client, data)
return await self._async_handle_auth_code(hass, client_id, data)

elif grant_type == 'refresh_token':
return await self._async_handle_refresh_token(hass, client, data)
return await self._async_handle_refresh_token(
hass, client_id, data)

return self.json({
'error': 'unsupported_grant_type',
}, status_code=400)

async def _async_handle_auth_code(self, hass, client, data):
async def _async_handle_auth_code(self, hass, client_id, data):
"""Handle authorization code request."""
code = data.get('code')

Expand All @@ -254,7 +265,7 @@ async def _async_handle_auth_code(self, hass, client, data):
'error': 'invalid_request',
}, status_code=400)

credentials = self._retrieve_credentials(client.id, code)
credentials = self._retrieve_credentials(client_id, code)

if credentials is None:
return self.json({
Expand All @@ -263,7 +274,7 @@ async def _async_handle_auth_code(self, hass, client, data):

user = await hass.auth.async_get_or_create_user(credentials)
refresh_token = await hass.auth.async_create_refresh_token(user,
client)
client_id)
access_token = hass.auth.async_create_access_token(refresh_token)

return self.json({
Expand All @@ -274,7 +285,7 @@ async def _async_handle_auth_code(self, hass, client, data):
int(refresh_token.access_token_expiration.total_seconds()),
})

async def _async_handle_refresh_token(self, hass, client, data):
async def _async_handle_refresh_token(self, hass, client_id, data):
"""Handle authorization code request."""
token = data.get('refresh_token')

Expand All @@ -285,7 +296,7 @@ async def _async_handle_refresh_token(self, hass, client, data):

refresh_token = await hass.auth.async_get_refresh_token(token)

if refresh_token is None or refresh_token.client_id != client.id:
if refresh_token is None or refresh_token.client_id != client_id:
return self.json({
'error': 'invalid_grant',
}, status_code=400)
Expand Down
79 changes: 0 additions & 79 deletions homeassistant/components/auth/client.py

This file was deleted.

Loading