User management (#15420)

* User management

* Lint

* Fix dict

* Reuse data instance

* OrderedDict all the way
This commit is contained in:
Paulus Schoutsen 2018-07-13 15:31:20 +02:00 committed by GitHub
parent 84858f5c19
commit 70fe463ef0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 982 additions and 116 deletions

View file

@ -7,6 +7,8 @@ import hmac
import voluptuous as vol
from homeassistant import data_entry_flow
from homeassistant.const import CONF_ID
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.auth.util import generate_secret
@ -16,8 +18,17 @@ from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS
STORAGE_VERSION = 1
STORAGE_KEY = 'auth_provider.homeassistant'
CONFIG_SCHEMA = AUTH_PROVIDER_SCHEMA.extend({
}, extra=vol.PREVENT_EXTRA)
def _disallow_id(conf):
"""Disallow ID in config."""
if CONF_ID in conf:
raise vol.Invalid(
'ID is not allowed for the homeassistant auth provider.')
return conf
CONFIG_SCHEMA = vol.All(AUTH_PROVIDER_SCHEMA, _disallow_id)
class InvalidAuth(HomeAssistantError):
@ -88,8 +99,8 @@ class Data:
hashed = base64.b64encode(hashed).decode()
return hashed
def add_user(self, username, password):
"""Add a user."""
def add_auth(self, username, password):
"""Add a new authenticated user/pass."""
if any(user['username'] == username for user in self.users):
raise InvalidUser
@ -98,8 +109,22 @@ class Data:
'password': self.hash_password(password, True),
})
@callback
def async_remove_auth(self, username):
"""Remove authentication."""
index = None
for i, user in enumerate(self.users):
if user['username'] == username:
index = i
break
if index is None:
raise InvalidUser
self.users.pop(index)
def change_password(self, username, new_password):
"""Update the password of a user.
"""Update the password.
Raises InvalidUser if user cannot be found.
"""
@ -121,16 +146,24 @@ class HassAuthProvider(AuthProvider):
DEFAULT_TITLE = 'Home Assistant Local'
data = None
async def async_initialize(self):
"""Initialize the auth provider."""
self.data = Data(self.hass)
await self.data.async_load()
async def async_credential_flow(self):
"""Return a flow to login."""
return LoginFlow(self)
async def async_validate_login(self, username, password):
"""Helper to validate a username and password."""
data = Data(self.hass)
await data.async_load()
if self.data is None:
await self.async_initialize()
await self.hass.async_add_executor_job(
data.validate_login, username, password)
self.data.validate_login, username, password)
async def async_get_or_create_credentials(self, flow_result):
"""Get credentials based on the flow result."""
@ -145,6 +178,24 @@ class HassAuthProvider(AuthProvider):
'username': username
})
async def async_user_meta_for_credentials(self, credentials):
"""Get extra info for this credential."""
return {
'name': credentials.data['username']
}
async def async_will_remove_credentials(self, credentials):
"""When credentials get removed, also remove the auth."""
if self.data is None:
await self.async_initialize()
try:
self.data.async_remove_auth(credentials.data['username'])
await self.data.async_save()
except InvalidUser:
# Can happen if somehow we didn't clean up a credential
pass
class LoginFlow(data_entry_flow.FlowHandler):
"""Handler for the login flow."""