From: Calin Crisan Date: Sun, 29 Mar 2015 14:39:51 +0000 (+0300) Subject: streaming authentication mode is now configurable X-Git-Url: http://www.vanbest.org/gitweb/?a=commitdiff_plain;h=74f73f67d047811fe2000348612f9efb27b97d30;p=motioneye-debian streaming authentication mode is now configurable --- diff --git a/src/config.py b/src/config.py index 818240f..9dd9a41 100644 --- a/src/config.py +++ b/src/config.py @@ -110,7 +110,7 @@ def get_main(as_lines=False): no_convert=['@admin_username', '@admin_password', '@normal_username', '@normal_password']) _get_additional_config(main_config, camera=False) - _set_default_motion(main_config, old_motion=_is_old_motion()) + _set_default_motion(main_config, old_motion=is_old_motion()) _main_config_cache = main_config @@ -121,7 +121,7 @@ def set_main(main_config): global _main_config_cache main_config = dict(main_config) - _set_default_motion(main_config, old_motion=_is_old_motion()) + _set_default_motion(main_config, old_motion=is_old_motion()) _main_config_cache = main_config main_config = dict(main_config) @@ -277,7 +277,7 @@ def get_camera(camera_id, as_lines=False): camera_config['@enabled'] = _CAMERA_CONFIG_FILE_NAME % {'id': camera_id} in threads camera_config['@id'] = camera_id - old_motion = _is_old_motion() + old_motion = is_old_motion() # adapt directives from old configuration, if needed if old_motion: @@ -331,7 +331,7 @@ def set_camera(camera_id, camera_config): camera_config = dict(camera_config) if utils.local_camera(camera_config): - old_motion = _is_old_motion() + old_motion = is_old_motion() # adapt directives to old configuration, if needed if old_motion: @@ -571,8 +571,8 @@ def camera_ui_to_dict(ui): '@webcam_resolution': max(1, int(ui['streaming_resolution'])), '@webcam_server_resize': ui['streaming_server_resize'], 'stream_motion': ui['streaming_motion'], - 'stream_auth_method': 2 if main_config['@normal_password'] else 0, - 'stream_authentication': (main_config['@normal_username'] + ':' + main_config['@normal_password']) if main_config['@normal_password'] else '', + 'stream_auth_method': {'disabled': 0, 'basic': 1, 'digest': 2}.get(ui['streaming_auth_mode'], 0), + 'stream_authentication': main_config['@normal_username'] + ':' + main_config['@normal_password'], # still images 'output_pictures': False, @@ -850,6 +850,7 @@ def camera_dict_to_ui(data): 'streaming_resolution': int(data['@webcam_resolution']), 'streaming_server_resize': data['@webcam_server_resize'], 'streaming_port': int(data['stream_port']), + 'streaming_auth_mode': {0: 'disabled', 1: 'basic', 2: 'digest'}.get(data.get('stream_auth_method'), 'disabled'), 'streaming_motion': int(data['stream_motion']), # still images @@ -1227,6 +1228,21 @@ def restore(content): return None +def is_old_motion(): + try: + binary, version = motionctl.find_motion() # @UnusedVariable + + if version.startswith('trunkREV'): # e.g. trunkREV599 + version = int(version[8:]) + return version < _LAST_OLD_CONFIG_VERSIONS[0] + + else: # stable release, should be in the format x.y.z + return update.compare_versions(version, _LAST_OLD_CONFIG_VERSIONS[1]) <= 0 + + except: + return False + + def _value_to_python(value): value_lower = value.lower() if value_lower == 'off': @@ -1402,21 +1418,6 @@ def _dict_to_conf(lines, data, list_names=[]): return lines -def _is_old_motion(): - try: - binary, version = motionctl.find_motion() # @UnusedVariable - - if version.startswith('trunkREV'): # e.g. trunkREV599 - version = int(version[8:]) - return version < _LAST_OLD_CONFIG_VERSIONS[0] - - else: # stable release, should be in the format x.y.z - return update.compare_versions(version, _LAST_OLD_CONFIG_VERSIONS[1]) <= 0 - - except: - return False - - def _set_default_motion(data, old_motion): data.setdefault('@enabled', True) diff --git a/src/handlers.py b/src/handlers.py index eeecc8a..696787b 100644 --- a/src/handlers.py +++ b/src/handlers.py @@ -154,7 +154,8 @@ class MainHandler(BaseHandler): main_sections=main_sections, camera_sections=camera_sections, hostname=socket.gethostname(), - admin_username=config.get_main().get('@admin_username')) + admin_username=config.get_main().get('@admin_username'), + old_motion=config.is_old_motion()) class ConfigHandler(BaseHandler): diff --git a/src/mjpgclient.py b/src/mjpgclient.py index 69cc86f..7e01b9b 100644 --- a/src/mjpgclient.py +++ b/src/mjpgclient.py @@ -108,9 +108,15 @@ class MjpgClient(iostream.IOStream): logging.debug('mjpg client for camera %(camera_id)s connected on port %(port)s' % { 'port': self._port, 'camera_id': self._camera_id}) - self.write('GET / HTTP/1.0\r\n\r\n') + if self._username: + auth_header = utils.build_basic_header(self._username, self._password) + self.write('GET / HTTP/1.0\r\n\r\nAuthorization: %s\r\n\r\n' % auth_header) + + else: + self.write('GET / HTTP/1.0\r\n\r\n') + self._seek_http() - + def _seek_http(self): if self._check_error(): return @@ -140,18 +146,32 @@ class MjpgClient(iostream.IOStream): if self._check_error(): return + m = re.match('Basic\s*realm="([a-zA-Z0-9\-\s]+)"', data.strip()) + if m: + logging.debug('mjpgclient: using basic authentication') + + auth_header = utils.build_basic_header(self._username, self._password) + self.write('GET / HTTP/1.0\r\n\r\nAuthorization: %s\r\n\r\n' % auth_header) + self._seek_http() + + return + m = re.match('Digest\s*realm="([a-zA-Z0-9\-\s]+)",\s*nonce="([a-zA-Z0-9]+)"', data.strip()) - if not m: - logging.error('mjpgclient: unknown authentication header: "%s"' % data) - return self._seek_content_length() + if m: + logging.debug('mjpgclient: using digest authentication') - realm, nonce = m.groups() - self._auth_digest_state['realm'] = realm - self._auth_digest_state['nonce'] = nonce + realm, nonce = m.groups() + self._auth_digest_state['realm'] = realm + self._auth_digest_state['nonce'] = nonce + + auth_header = utils.build_digest_header('GET', '/', self._username, self._password, self._auth_digest_state) + self.write('GET / HTTP/1.0\r\n\r\nAuthorization: %s\r\n\r\n' % auth_header) + self._seek_http() + + return - auth_header = utils.build_digest_header('GET', '/', self._username, self._password, self._auth_digest_state) - self.write('GET / HTTP/1.0\r\n\r\nAuthorization: %s\r\n\r\n' % auth_header) - self._seek_http() + logging.error('mjpgclient: unknown authentication header: "%s"' % data) + self._seek_content_length() def _seek_content_length(self): if self._check_error(): @@ -255,7 +275,7 @@ def get_jpg(camera_id): port = camera_config['stream_port'] username, password = None, None - if camera_config.get('stream_auth_method') == 2: + if camera_config.get('stream_auth_method') > 0: username, password = camera_config.get('stream_authentication', ':').split(':') client = MjpgClient(camera_id, port, username, password) diff --git a/src/utils.py b/src/utils.py index 73aacd3..101a881 100644 --- a/src/utils.py +++ b/src/utils.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import base64 import datetime import hashlib import logging @@ -348,6 +349,10 @@ def compute_signature(method, uri, body, key): return hashlib.sha1('%s:%s:%s:%s' % (method, uri, body or '', key)).hexdigest().lower() +def build_basic_header(username, password): + return 'Basic ' + base64.encodestring('%s:%s' % (username, password)).replace('\n', '') + + def build_digest_header(method, url, username, password, state): realm = state['realm'] nonce = state['nonce'] diff --git a/static/js/main.js b/static/js/main.js index c2b9926..cf1a074 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -1202,6 +1202,7 @@ function cameraUi2Dict() { 'streaming_resolution': $('#streamingResolutionSlider').val(), 'streaming_server_resize': $('#streamingServerResizeSwitch')[0].checked, 'streaming_port': $('#streamingPortEntry').val(), + 'streaming_auth_mode': $('#streamingAuthModeSelect').val() || 'disabled', /* compatibility with old motion */ 'streaming_motion': $('#streamingMotion')[0].checked, /* still images */ @@ -1456,6 +1457,7 @@ function dict2CameraUi(dict) { $('#streamingResolutionSlider').val(dict['streaming_resolution']); $('#streamingServerResizeSwitch')[0].checked = dict['streaming_server_resize']; $('#streamingPortEntry').val(dict['streaming_port']); + $('#streamingAuthModeSelect').val(dict['streaming_auth_mode']); $('#streamingMotion')[0].checked = dict['streaming_motion']; var cameraUrl = location.protocol + '//' + location.host + '/picture/' + dict.id + '/'; diff --git a/templates/main.html b/templates/main.html index 291f66c..f3eed0a 100644 --- a/templates/main.html +++ b/templates/main.html @@ -408,11 +408,27 @@ ? + {% if not old_motion %} + + Authentication Mode + + + + ? + + {% endif %} Motion Optimization ? + +
+ Snapshot URL