From: Calin Crisan Date: Sat, 5 Aug 2017 14:17:15 +0000 (+0300) Subject: add support for HTTP basic auth X-Git-Url: http://www.vanbest.org/gitweb/?a=commitdiff_plain;h=714d1819d4ee78adc4bda84d551ab6e78bc51c64;p=motioneye-debian add support for HTTP basic auth --- diff --git a/extra/motioneye.conf.sample b/extra/motioneye.conf.sample index a274d5a..5a90428 100644 --- a/extra/motioneye.conf.sample +++ b/extra/motioneye.conf.sample @@ -89,3 +89,6 @@ timelapse_timeout 500 # enable adding and removing cameras from UI add_remove_cameras true + +# enables HTTP basic authentication scheme (in addition to, not instead of the signature mechanism) +http_basic_auth false diff --git a/motioneye/handlers.py b/motioneye/handlers.py index bee0959..0baab08 100644 --- a/motioneye/handlers.py +++ b/motioneye/handlers.py @@ -115,28 +115,49 @@ class BaseHandler(RequestHandler): username = self.get_argument('_username', None) signature = self.get_argument('_signature', None) login = self.get_argument('_login', None) == 'true' - if (username == main_config.get('@admin_username') and + + admin_username = main_config.get('@admin_username') + normal_username = main_config.get('@normal_username') + + admin_password = main_config.get('@admin_password') + normal_password = main_config.get('@normal_password') + + admin_hash = hashlib.sha1(main_config['@admin_password']).hexdigest() + normal_hash = hashlib.sha1(main_config['@normal_password']).hexdigest() + + if settings.HTTP_BASIC_AUTH and 'Authorization' in self.request.headers: + up = utils.parse_basic_header(self.request.headers['Authorization']) + if up: + if (up['username'] == admin_username and + admin_password in (up['password'], hashlib.sha1(up['password']).hexdigest())): + + return 'admin' + + if (up['username'] == normal_username and + normal_password in (up['password'], hashlib.sha1(up['password']).hexdigest())): + + return 'normal' + + if (username == admin_username and (signature == utils.compute_signature(self.request.method, self.request.uri, - self.request.body, main_config['@admin_password']) or + self.request.body, admin_password) or signature == utils.compute_signature(self.request.method, self.request.uri, - self.request.body, - hashlib.sha1(main_config['@admin_password']).hexdigest()))): + self.request.body, admin_hash))): return 'admin' - elif not username and not main_config.get('@normal_password'): # no authentication required for normal user + if not username and not normal_password: # no authentication required for normal user return 'normal' - - elif (username == main_config.get('@normal_username') and - (signature == utils.compute_signature(self.request.method, self.request.uri, - self.request.body, main_config.get('@normal_password')) or - signature == utils.compute_signature(self.request.method, self.request.uri, - self.request.body, - hashlib.sha1(main_config['@normal_password']).hexdigest()))): + + if (username == normal_username and + (signature == utils.compute_signature(self.request.method, self.request.uri, + self.request.body, normal_password) or + signature == utils.compute_signature(self.request.method, self.request.uri, + self.request.body, normal_hash))): return 'normal' - elif username and username != '_' and login: + if username and username != '_' and login: logging.error('authentication failed for user %(user)s' % {'user': username}) return None diff --git a/motioneye/settings.py b/motioneye/settings.py index 10d32f4..999c6b8 100644 --- a/motioneye/settings.py +++ b/motioneye/settings.py @@ -128,3 +128,6 @@ VALIDATE_CERTS = True # an external program to be executed whenever a password changes; # the program will be invoked with environment variables MEYE_USERNAME and MEYE_PASSWORD PASSWORD_HOOK = None + +# enables HTTP basic authentication scheme (in addition to, not instead of the signature mechanism) +HTTP_BASIC_AUTH = False diff --git a/motioneye/utils.py b/motioneye/utils.py index d8456e2..e00b3d6 100644 --- a/motioneye/utils.py +++ b/motioneye/utils.py @@ -683,6 +683,32 @@ def build_basic_header(username, password): return 'Basic ' + base64.encodestring('%s:%s' % (username, password)).replace('\n', '') +def parse_basic_header(header): + parts = header.split(' ', 1) + if len(parts) < 2: + return None + + if parts[0].lower() != 'basic': + return None + + encoded = parts[1] + + try: + decoded = base64.decodestring(encoded) + + except: + return None + + parts = decoded.split(':', 1) + if len(parts) < 2: + return None + + return { + 'username': parts[0], + 'password': parts[1] + } + + def build_digest_header(method, url, username, password, state): realm = state['realm'] nonce = state['nonce']