1
0
mirror of https://github.com/binary-kitchen/doorlockd synced 2024-12-22 18:34:25 +01:00
doorlockd-mirror/pydoorlock/Authenticator.py
Thomas Schmid 03e27930ce update nuki state handling
update state handling so that manual state changes of the nuki (key or
button press) are reflected to the internal doorlock state. Refactor
general state handling to support this

Signed-off-by: Thomas Schmid <tom@lfence.de>
2024-01-03 21:32:22 +01:00

117 lines
3.8 KiB
Python

"""
Doorlockd -- Binary Kitchen's smart door opener
Copyright (c) Binary Kitchen e.V., 2018
Author:
Ralf Ramsauer <ralf@binary-kitchen.de>
This work is licensed under the terms of the GNU GPL, version 2. See
the LICENSE file in the top-level directory.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
"""
import hashlib
import ldap
import logging
from enum import Enum
from .DoorlockResponse import DoorlockResponse
log = logging.getLogger()
class AuthMethod(Enum):
LDAP_USER_PW = 1
LOCAL_USER_DB = 2
def __str__(self):
if self == AuthMethod.LDAP_USER_PW:
return 'LDAP'
elif self == AuthMethod.LOCAL_USER_DB:
return 'Local'
return 'Error'
class Authenticator:
def __init__(self, cfg):
self._simulate = cfg.boolean('SIMULATE_AUTH')
self._backends = set()
if self._simulate:
return
self._ldap_uri = cfg.str('LDAP_URI')
self._ldap_binddn = cfg.str('LDAP_BINDDN')
if self._ldap_uri and self._ldap_binddn:
log.info('Initialising LDAP auth backend')
self._backends.add(AuthMethod.LDAP_USER_PW)
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
ldap.set_option(ldap.OPT_REFERRALS, 0)
file_local_db = cfg.str('LOCAL_USER_DB')
if file_local_db:
log.info('Initialising local auth backend')
self._local_db = dict()
with open(file_local_db, 'r') as f:
for line in f:
line = line.split()
user = line[0]
pwd = line[1].split(':')
self._local_db[user] = pwd
self._backends.add(AuthMethod.LOCAL_USER_DB)
@property
def backends(self):
return self._backends
def _try_auth_local(self, user, password):
log.info(' Trying to local auth (user, password) as user %s', user)
if user not in self._local_db:
log.info(' No user %s in local database', user)
return DoorlockResponse.Perm
stored_pw = self._local_db[user][0]
stored_salt = self._local_db[user][1]
if stored_pw == hashlib.sha256(stored_salt.encode() + password.encode()).hexdigest():
log.info(' Authenticated as user %s', user)
return DoorlockResponse.Success
log.info(' Invalid credentials')
return DoorlockResponse.Perm
def _try_auth_ldap(self, user, password):
log.info(' Trying to LDAP auth (user, password) as user %s', user)
ldap_username = self._ldap_binddn % user
try:
l = ldap.initialize(self._ldap_uri)
l.simple_bind_s(ldap_username, password)
l.unbind_s()
except ldap.INVALID_CREDENTIALS:
log.info(' Invalid credentials')
return DoorlockResponse.Perm
except ldap.LDAPError as e:
log.info(' LDAP Error: %s' % e)
return DoorlockResponse.InternalError
log.info(' Authenticated as user %s', user)
return DoorlockResponse.Success
def try_auth(self, credentials):
if self._simulate:
log.info('SIMULATION MODE! ACCEPTING ANYTHING!')
return DoorlockResponse.Success
if AuthMethod.LDAP_USER_PW in self._backends:
retval = self._try_auth_ldap(credentials[0], credentials[1])
if retval == DoorlockResponse.Success:
return retval
if AuthMethod.LOCAL_USER_DB in self._backends:
return self._try_auth_local(credentials[0], credentials[1])
return DoorlockResponse.Perm