def get_client_name(request: HttpRequest, is_browser_view: bool) -> str: # If the API request specified a client in the request content, # that has priority. Otherwise, extract the client from the # User-Agent. if 'client' in request.GET: return request.GET['client'] if 'client' in request.POST: return request.POST['client'] if "HTTP_USER_AGENT" in request.META: user_agent = parse_user_agent(request.META["HTTP_USER_AGENT"]) # type: Optional[Dict[str, str]] else: user_agent = None if user_agent is not None: # We could check for a browser's name being "Mozilla", but # e.g. Opera and MobileSafari don't set that, and it seems # more robust to just key off whether it was a browser view if is_browser_view and not user_agent["name"].startswith("Zulip"): # Avoid changing the client string for browsers, but let # the Zulip desktop and mobile apps be themselves. return "website" else: return user_agent["name"] else: # In the future, we will require setting USER_AGENT, but for # now we just want to tag these requests so we can review them # in logs and figure out the extent of the problem if is_browser_view: return "website" else: return "Unspecified"
def get_client_name(request, is_json_view): # If the API request specified a client in the request content, # that has priority. Otherwise, extract the client from the # User-Agent. if 'client' in request.REQUEST: return request.REQUEST['client'] elif "HTTP_USER_AGENT" in request.META: user_agent = parse_user_agent(request.META["HTTP_USER_AGENT"]) # We could check for a browser's name being "Mozilla", but # e.g. Opera and MobileSafari don't set that, and it seems # more robust to just key off whether it was a json view if user_agent["name"] != "ZulipDesktop" and is_json_view: # Avoid changing the client string for browsers Once this # is out to prod, we can name the field to something like # Browser for consistency. return "website" else: return user_agent["name"] else: # In the future, we will require setting USER_AGENT, but for # now we just want to tag these requests so we can review them # in logs and figure out the extent of the problem if is_json_view: return "website" else: return "Unspecified"
def check_global_compatibility(request: HttpRequest) -> HttpResponse: if request.META.get('HTTP_USER_AGENT') is None: return json_error(_('User-Agent header missing from request')) # This string should not be tagged for translation, since old # clients are checking for an extra string. legacy_compatibility_error_message = "Client is too old" user_agent = parse_user_agent(request.META["HTTP_USER_AGENT"]) if user_agent['name'] == "ZulipInvalid": return json_error(legacy_compatibility_error_message) if user_agent['name'] == "ZulipMobile": user_os = find_mobile_os(request.META["HTTP_USER_AGENT"]) if (user_os == 'android' and version_lt(user_agent['version'], android_min_app_version)): return json_error(legacy_compatibility_error_message) return json_success()
def get_client_name(request: HttpRequest) -> str: # If the API request specified a client in the request content, # that has priority. Otherwise, extract the client from the # User-Agent. if 'client' in request.GET: # nocoverage return request.GET['client'] if 'client' in request.POST: return request.POST['client'] if "HTTP_USER_AGENT" in request.META: user_agent: Optional[Dict[str, str]] = parse_user_agent( request.META["HTTP_USER_AGENT"]) else: user_agent = None if user_agent is not None: return user_agent["name"] # In the future, we will require setting USER_AGENT, but for # now we just want to tag these requests so we can review them # in logs and figure out the extent of the problem return "Unspecified"
def test_user_agent_parsing(self): # type: () -> None """Test for our user agent parsing logic, using a large data set.""" user_agents_parsed = defaultdict(int) # type: Dict[str, int] user_agents_path = os.path.join(settings.DEPLOY_ROOT, "zerver/fixtures/user_agents_unique") parse_errors = [] for line in open(user_agents_path).readlines(): line = line.strip() match = re.match('^(?P<count>[0-9]+) "(?P<user_agent>.*)"$', line) self.assertIsNotNone(match) groupdict = match.groupdict() count = groupdict["count"] user_agent = groupdict["user_agent"] ret = parse_user_agent(user_agent) self.assertIsNotNone(ret) if ret is None: # nocoverage parse_errors.append(line) continue user_agents_parsed[ret["name"]] += int(count) self.assertEqual(len(parse_errors), 0)
def test_user_agent_parsing(self): # type: () -> None """Test for our user agent parsing logic, using a large data set.""" user_agents_parsed = defaultdict(int) # type: Dict[str, int] user_agents_path = os.path.join(settings.DEPLOY_ROOT, "zerver/fixtures/user_agents_unique") parse_errors = [] for line in open(user_agents_path).readlines(): line = line.strip() match = re.match('^(?P<count>[0-9]+) "(?P<user_agent>.*)"$', line) self.assertIsNotNone(match) groupdict = match.groupdict() count = groupdict["count"] user_agent = groupdict["user_agent"] ret = parse_user_agent(user_agent) self.assertIsNotNone(ret) if ret is None: # nocoverage parse_errors.append(line) continue user_agents_parsed[ret["name"]] += int(count) self.assertEqual(len(parse_errors), 0)
def is_outdated_desktop_app(user_agent_str: str) -> Tuple[bool, bool, bool]: # Returns (insecure, banned, auto_update_broken user_agent = parse_user_agent(user_agent_str) if user_agent['name'] == 'ZulipDesktop': # The deprecated QT/webkit based desktop app, last updated in ~2016. return (True, True, True) if user_agent['name'] != 'ZulipElectron': return (False, False, False) if version_lt(user_agent['version'], '4.0.0'): # Version 2.3.82 and older (aka <4.0.0) of the modern # Electron-based Zulip desktop app with known security issues. # won't auto-update; we may want a special notice to # distinguish those from modern releases. return (True, True, True) if version_lt(user_agent['version'], '4.0.3'): # Other insecure versions should just warn. return (True, False, False) return (False, False, False)
def start_social_login(request: HttpRequest, backend: str, extra_arg: Optional[str] = None) -> HttpResponse: user_agent = parse_user_agent( request.META.get("HTTP_USER_AGENT", "Missing User-Agent")) if user_agent["name"] == "ZulipElectron": return render(request, "zerver/desktop_login.html") backend_url = reverse('social:begin', args=[backend]) extra_url_params: Dict[str, str] = {} if backend == "saml": result = SAMLAuthBackend.check_config() if result is not None: return result # This backend requires the name of the IdP (from the list of configured ones) # to be passed as the parameter. if not extra_arg or extra_arg not in settings.SOCIAL_AUTH_SAML_ENABLED_IDPS: logging.info( "Attempted to initiate SAML authentication with wrong idp argument: %s", extra_arg) return redirect_to_config_error("saml") extra_url_params = {'idp': extra_arg} # TODO: Add AzureAD also. if backend in ["github", "google", "gitlab"]: key_setting = "SOCIAL_AUTH_" + backend.upper() + "_KEY" secret_setting = "SOCIAL_AUTH_" + backend.upper() + "_SECRET" if not (getattr(settings, key_setting) and getattr(settings, secret_setting)): return redirect_to_config_error(backend) return oauth_redirect_to_root(request, backend_url, 'social', extra_url_params=extra_url_params)
def check_compatibility(request: HttpRequest) -> HttpResponse: user_agent = parse_user_agent(request.META["HTTP_USER_AGENT"]) if user_agent['name'] == "ZulipInvalid": return json_error("Client is too old") return json_success()
def check_compatibility(request: HttpRequest) -> HttpResponse: user_agent = parse_user_agent(request.META["HTTP_USER_AGENT"]) if user_agent is None or user_agent['name'] == "ZulipInvalid": return json_error("Client is too old") return json_success()
import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) from zerver.lib.user_agent import parse_user_agent user_agents_parsed = defaultdict(int) # type: Dict[str, int] user_agents_path = os.path.join(os.path.dirname(__file__), "user_agents_unique") parse_errors = 0 for line in open(user_agents_path).readlines(): line = line.strip() match = re.match('^(?P<count>[0-9]+) "(?P<user_agent>.*)"$', line) if match is None: print(line) continue groupdict = match.groupdict() count = groupdict["count"] user_agent = groupdict["user_agent"] ret = parse_user_agent(user_agent) if ret is None: print("parse error", line) parse_errors += 1 continue user_agents_parsed[ret["name"]] += int(count) for key in user_agents_parsed: print(" ", key, user_agents_parsed[key]) print("%s parse errors!" % (parse_errors, ))
def check_server_incompatibility(request: HttpRequest) -> bool: user_agent = parse_user_agent(request.META.get("HTTP_USER_AGENT", "Missing User-Agent")) return user_agent['name'] == "ZulipInvalid"
def check_server_incompatibility(request: HttpRequest) -> bool: user_agent = parse_user_agent( request.headers.get("User-Agent", "Missing User-Agent")) return user_agent["name"] == "ZulipInvalid"
def wrapper(request: HttpRequest, *args: object, **kwargs: object) -> HttpResponse: user_agent = parse_user_agent(request.META.get("HTTP_USER_AGENT", "Missing User-Agent")) if user_agent["name"] == "ZulipElectron": return render(request, "zerver/desktop_login.html") return func(request, *args, **kwargs)
import os import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) from zerver.lib.user_agent import parse_user_agent user_agents_parsed = defaultdict(int) # type: Dict[str, int] user_agents_path = os.path.join(os.path.dirname(__file__), "user_agents_unique") parse_errors = 0 for line in open(user_agents_path).readlines(): line = line.strip() match = re.match('^(?P<count>[0-9]+) "(?P<user_agent>.*)"$', line) if match is None: print(line) continue groupdict = match.groupdict() count = groupdict["count"] user_agent = groupdict["user_agent"] ret = parse_user_agent(user_agent) if ret is None: print("parse error", line) parse_errors += 1 continue user_agents_parsed[ret["name"]] += int(count) for key in user_agents_parsed: print(" ", key, user_agents_parsed[key]) print("%s parse errors!" % (parse_errors,))
def check_server_incompatibility(request: HttpRequest) -> bool: user_agent = parse_user_agent(request.META.get("HTTP_USER_AGENT", "Missing User-Agent")) return user_agent["name"] == "ZulipInvalid"
def check_server_incompatibility(request: HttpRequest) -> bool: user_agent = parse_user_agent(request.META["HTTP_USER_AGENT"]) return user_agent['name'] == "ZulipInvalid"