This commit is contained in:
Paulus Schoutsen 2019-07-31 12:25:30 -07:00
parent da05dfe708
commit 4de97abc3a
2676 changed files with 163166 additions and 140084 deletions

View file

@ -71,8 +71,7 @@ import voluptuous as vol
from homeassistant import data_entry_flow
from homeassistant.components.http import KEY_REAL_IP
from homeassistant.components.http.ban import process_wrong_login, \
log_invalid_auth
from homeassistant.components.http.ban import process_wrong_login, log_invalid_auth
from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.components.http.view import HomeAssistantView
from . import indieauth
@ -81,56 +80,55 @@ from . import indieauth
async def async_setup(hass, store_result):
"""Component to allow users to login."""
hass.http.register_view(AuthProvidersView)
hass.http.register_view(
LoginFlowIndexView(hass.auth.login_flow, store_result))
hass.http.register_view(
LoginFlowResourceView(hass.auth.login_flow, store_result))
hass.http.register_view(LoginFlowIndexView(hass.auth.login_flow, store_result))
hass.http.register_view(LoginFlowResourceView(hass.auth.login_flow, store_result))
class AuthProvidersView(HomeAssistantView):
"""View to get available auth providers."""
url = '/auth/providers'
name = 'api:auth:providers'
url = "/auth/providers"
name = "api:auth:providers"
requires_auth = False
async def get(self, request):
"""Get available auth providers."""
hass = request.app['hass']
hass = request.app["hass"]
if not hass.components.onboarding.async_is_user_onboarded():
return self.json_message(
message='Onboarding not finished',
message="Onboarding not finished",
status_code=400,
message_code='onboarding_required'
message_code="onboarding_required",
)
return self.json([{
'name': provider.name,
'id': provider.id,
'type': provider.type,
} for provider in hass.auth.auth_providers])
return self.json(
[
{"name": provider.name, "id": provider.id, "type": provider.type}
for provider in hass.auth.auth_providers
]
)
def _prepare_result_json(result):
"""Convert result to JSON."""
if result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
if result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
data = result.copy()
data.pop('result')
data.pop('data')
data.pop("result")
data.pop("data")
return data
if result['type'] != data_entry_flow.RESULT_TYPE_FORM:
if result["type"] != data_entry_flow.RESULT_TYPE_FORM:
return result
import voluptuous_serialize
data = result.copy()
schema = data['data_schema']
schema = data["data_schema"]
if schema is None:
data['data_schema'] = []
data["data_schema"] = []
else:
data['data_schema'] = voluptuous_serialize.convert(schema)
data["data_schema"] = voluptuous_serialize.convert(schema)
return data
@ -138,8 +136,8 @@ def _prepare_result_json(result):
class LoginFlowIndexView(HomeAssistantView):
"""View to create a config flow."""
url = '/auth/login_flow'
name = 'api:auth:login_flow'
url = "/auth/login_flow"
name = "api:auth:login_flow"
requires_auth = False
def __init__(self, flow_mgr, store_result):
@ -151,39 +149,45 @@ class LoginFlowIndexView(HomeAssistantView):
"""Do not allow index of flows in progress."""
return web.Response(status=405)
@RequestDataValidator(vol.Schema({
vol.Required('client_id'): str,
vol.Required('handler'): vol.Any(str, list),
vol.Required('redirect_uri'): str,
vol.Optional('type', default='authorize'): str,
}))
@RequestDataValidator(
vol.Schema(
{
vol.Required("client_id"): str,
vol.Required("handler"): vol.Any(str, list),
vol.Required("redirect_uri"): str,
vol.Optional("type", default="authorize"): str,
}
)
)
@log_invalid_auth
async def post(self, request, data):
"""Create a new login flow."""
if not await indieauth.verify_redirect_uri(
request.app['hass'], data['client_id'], data['redirect_uri']):
return self.json_message('invalid client id or redirect uri', 400)
request.app["hass"], data["client_id"], data["redirect_uri"]
):
return self.json_message("invalid client id or redirect uri", 400)
if isinstance(data['handler'], list):
handler = tuple(data['handler'])
if isinstance(data["handler"], list):
handler = tuple(data["handler"])
else:
handler = data['handler']
handler = data["handler"]
try:
result = await self._flow_mgr.async_init(
handler, context={
'ip_address': request[KEY_REAL_IP],
'credential_only': data.get('type') == 'link_user',
})
handler,
context={
"ip_address": request[KEY_REAL_IP],
"credential_only": data.get("type") == "link_user",
},
)
except data_entry_flow.UnknownHandler:
return self.json_message('Invalid handler specified', 404)
return self.json_message("Invalid handler specified", 404)
except data_entry_flow.UnknownStep:
return self.json_message('Handler does not support init', 400)
return self.json_message("Handler does not support init", 400)
if result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
result.pop('data')
result['result'] = self._store_result(
data['client_id'], result['result'])
if result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
result.pop("data")
result["result"] = self._store_result(data["client_id"], result["result"])
return self.json(result)
return self.json(_prepare_result_json(result))
@ -192,8 +196,8 @@ class LoginFlowIndexView(HomeAssistantView):
class LoginFlowResourceView(HomeAssistantView):
"""View to interact with the flow manager."""
url = '/auth/login_flow/{flow_id}'
name = 'api:auth:login_flow:resource'
url = "/auth/login_flow/{flow_id}"
name = "api:auth:login_flow:resource"
requires_auth = False
def __init__(self, flow_mgr, store_result):
@ -203,44 +207,43 @@ class LoginFlowResourceView(HomeAssistantView):
async def get(self, request):
"""Do not allow getting status of a flow in progress."""
return self.json_message('Invalid flow specified', 404)
return self.json_message("Invalid flow specified", 404)
@RequestDataValidator(vol.Schema({
'client_id': str
}, extra=vol.ALLOW_EXTRA))
@RequestDataValidator(vol.Schema({"client_id": str}, extra=vol.ALLOW_EXTRA))
@log_invalid_auth
async def post(self, request, flow_id, data):
"""Handle progressing a login flow request."""
client_id = data.pop('client_id')
client_id = data.pop("client_id")
if not indieauth.verify_client_id(client_id):
return self.json_message('Invalid client id', 400)
return self.json_message("Invalid client id", 400)
try:
# do not allow change ip during login flow
for flow in self._flow_mgr.async_progress():
if (flow['flow_id'] == flow_id and
flow['context']['ip_address'] !=
request.get(KEY_REAL_IP)):
return self.json_message('IP address changed', 400)
if flow["flow_id"] == flow_id and flow["context"][
"ip_address"
] != request.get(KEY_REAL_IP):
return self.json_message("IP address changed", 400)
result = await self._flow_mgr.async_configure(flow_id, data)
except data_entry_flow.UnknownFlow:
return self.json_message('Invalid flow specified', 404)
return self.json_message("Invalid flow specified", 404)
except vol.Invalid:
return self.json_message('User input malformed', 400)
return self.json_message("User input malformed", 400)
if result['type'] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
if result["type"] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
# @log_invalid_auth does not work here since it returns HTTP 200
# need manually log failed login attempts
if (result.get('errors') is not None and
result['errors'].get('base') in ['invalid_auth',
'invalid_code']):
if result.get("errors") is not None and result["errors"].get("base") in [
"invalid_auth",
"invalid_code",
]:
await process_wrong_login(request)
return self.json(_prepare_result_json(result))
result.pop('data')
result['result'] = self._store_result(client_id, result['result'])
result.pop("data")
result["result"] = self._store_result(client_id, result["result"])
return self.json(result)
@ -249,6 +252,6 @@ class LoginFlowResourceView(HomeAssistantView):
try:
self._flow_mgr.async_abort(flow_id)
except data_entry_flow.UnknownFlow:
return self.json_message('Invalid flow specified', 404)
return self.json_message("Invalid flow specified", 404)
return self.json_message('Flow aborted')
return self.json_message("Flow aborted")