def _wrapped_func_arguments(request, *args, **kwargs): # type: (HttpRequest, *Any, **Any) -> HttpResponse # First try block attempts to get the credentials we need to do authentication try: # Grab the base64-encoded authentication string, decode it, and split it into # the email and API key auth_type, credentials = request.META['HTTP_AUTHORIZATION'].split() # case insensitive per RFC 1945 if auth_type.lower() != "basic": return json_error(_("This endpoint requires HTTP basic authentication.")) role, api_key = base64.b64decode(force_bytes(credentials)).decode('utf-8').split(":") except ValueError: return json_unauthorized(_("Invalid authorization header for basic auth")) except KeyError: return json_unauthorized("Missing authorization header for basic auth") # Now we try to do authentication or die try: # profile is a Union[UserProfile, RemoteZulipServer] profile = validate_api_key(request, role, api_key, is_webhook) except JsonableError as e: return json_unauthorized(e.error) request.user = profile if is_remote_server(role): assert isinstance(profile, RemoteZulipServer) # type: ignore # https://github.com/python/mypy/issues/2957 request._email = "zulip-server:" + role profile.rate_limits = "" process_client(request, profile, remote_server_request=True) else: assert isinstance(profile, UserProfile) # type: ignore # https://github.com/python/mypy/issues/2957 request._email = profile.email process_client(request, profile) # Apply rate limiting return rate_limit()(view_func)(request, profile, *args, **kwargs)
def validate_api_key(request: HttpRequest, role: Optional[str], api_key: str, is_webhook: bool=False, client_name: Optional[str]=None) -> Union[UserProfile, RemoteZulipServer]: # Remove whitespace to protect users from trivial errors. api_key = api_key.strip() if role is not None: role = role.strip() if settings.ZILENCER_ENABLED and role is not None and is_remote_server(role): try: remote_server = get_remote_server_by_uuid(role) except RemoteZulipServer.DoesNotExist: raise InvalidZulipServerError(role) if api_key != remote_server.api_key: raise InvalidZulipServerKeyError(role) if get_subdomain(request) != Realm.SUBDOMAIN_FOR_ROOT_DOMAIN: raise JsonableError(_("Invalid subdomain for push notifications bouncer")) request.user = remote_server request._email = "zulip-server:" + role remote_server.rate_limits = "" process_client(request, remote_server, remote_server_request=True) return remote_server user_profile = access_user_by_api_key(request, api_key, email=role) if user_profile.is_incoming_webhook and not is_webhook: raise JsonableError(_("This API is not available to incoming webhook bots.")) request.user = user_profile request._email = user_profile.email process_client(request, user_profile, client_name=client_name) return user_profile
def _wrapped_func_arguments(request, *args, **kwargs): # type: (HttpRequest, *Any, **Any) -> HttpResponse # First try block attempts to get the credentials we need to do authentication try: # Grab the base64-encoded authentication string, decode it, and split it into # the email and API key auth_type, credentials = request.META['HTTP_AUTHORIZATION'].split() # case insensitive per RFC 1945 if auth_type.lower() != "basic": return json_error(_("This endpoint requires HTTP basic authentication.")) role, api_key = base64.b64decode(force_bytes(credentials)).decode('utf-8').split(":") except ValueError: return json_unauthorized(_("Invalid authorization header for basic auth")) except KeyError: return json_unauthorized("Missing authorization header for basic auth") # Now we try to do authentication or die try: # profile is a Union[UserProfile, RemoteZulipServer] profile = validate_api_key(request, role, api_key, is_webhook) except JsonableError as e: return json_unauthorized(e.error) request.user = profile if is_remote_server(role): assert isinstance(profile, RemoteZulipServer) # type: ignore # https://github.com/python/mypy/issues/2957 request._email = "zulip-server:" + role profile.rate_limits = "" process_client(request, profile, remote_server_request=True) else: assert isinstance(profile, UserProfile) # type: ignore # https://github.com/python/mypy/issues/2957 request._email = profile.email process_client(request, profile) # Apply rate limiting return rate_limit()(view_func)(request, profile, *args, **kwargs)
def validate_api_key(request, role, api_key, is_webhook=False, client_name=None): # type: (HttpRequest, Optional[Text], Text, bool, Optional[Text]) -> Union[UserProfile, RemoteZulipServer] # Remove whitespace to protect users from trivial errors. api_key = api_key.strip() if role is not None: role = role.strip() if settings.ZILENCER_ENABLED and role is not None and is_remote_server(role): try: remote_server = get_remote_server_by_uuid(role) except RemoteZulipServer.DoesNotExist: raise JsonableError(_("Invalid Zulip server: %s") % (role,)) if api_key != remote_server.api_key: raise JsonableError(_("Invalid API key")) if not check_subdomain(get_subdomain(request), ""): raise JsonableError(_("This API key only works on the root subdomain")) request.user = remote_server request._email = "zulip-server:" + role remote_server.rate_limits = "" process_client(request, remote_server, remote_server_request=True) return remote_server user_profile = access_user_by_api_key(request, api_key, email=role) if user_profile.is_incoming_webhook and not is_webhook: raise JsonableError(_("This API is not available to incoming webhook bots.")) request.user = user_profile request._email = user_profile.email process_client(request, user_profile, client_name=client_name) return user_profile
def validate_api_key(request, role, api_key, is_webhook=False): # type: (HttpRequest, Text, Text, bool) -> Union[UserProfile, RemoteZulipServer] # Remove whitespace to protect users from trivial errors. role, api_key = role.strip(), api_key.strip() if not is_remote_server(role): try: profile = get_user_profile_by_email( role) # type: Union[UserProfile, RemoteZulipServer] except UserProfile.DoesNotExist: raise JsonableError(_("Invalid user: %s") % (role, )) else: try: profile = get_remote_server_by_uuid(role) except RemoteZulipServer.DoesNotExist: raise JsonableError(_("Invalid Zulip server: %s") % (role, )) if api_key != profile.api_key: if len(api_key) != 32: reason = _("Incorrect API key length (keys should be 32 " "characters long) for role '%s'") else: reason = _("Invalid API key for role '%s'") raise JsonableError(reason % (role, )) # early exit for RemoteZulipServer instances if settings.ZILENCER_ENABLED and isinstance(profile, RemoteZulipServer): if not check_subdomain(get_subdomain(request), ""): raise JsonableError( _("This API key only works on the root subdomain")) return profile profile = cast(UserProfile, profile) # is UserProfile if not profile.is_active: raise JsonableError(_("Account not active")) if profile.is_incoming_webhook and not is_webhook: raise JsonableError(_("Account is not valid to post webhook messages")) if profile.realm.deactivated: raise JsonableError(_("Realm for account has been deactivated")) if (not check_subdomain(get_subdomain(request), profile.realm.subdomain) and # Allow access to localhost for Tornado not (settings.RUNNING_INSIDE_TORNADO and request.META["SERVER_NAME"] == "127.0.0.1" and request.META["REMOTE_ADDR"] == "127.0.0.1")): logging.warning( "User %s attempted to access API on wrong subdomain %s" % (profile.email, get_subdomain(request))) raise JsonableError(_("Account is not associated with this subdomain")) return profile
def api_auth(self, identifier): # type: (Text) -> Dict[str, Text] """ identifier: Can be an email or a remote server uuid. """ if is_remote_server(identifier): api_key = self.get_server_api_key(identifier) else: api_key = self.get_api_key(identifier) credentials = u"%s:%s" % (identifier, api_key) return { 'HTTP_AUTHORIZATION': u'Basic ' + base64.b64encode(credentials.encode('utf-8')).decode('utf-8') }
def api_auth(self, identifier): # type: (Text) -> Dict[str, Text] """ identifier: Can be an email or a remote server uuid. """ if is_remote_server(identifier): api_key = self.get_server_api_key(identifier) else: api_key = self.get_api_key(identifier) credentials = u"%s:%s" % (identifier, api_key) return { 'HTTP_AUTHORIZATION': u'Basic ' + base64.b64encode(credentials.encode('utf-8')).decode('utf-8') }
def validate_api_key(request, role, api_key, is_webhook=False): # type: (HttpRequest, Text, Text, bool) -> Union[UserProfile, RemoteZulipServer] # Remove whitespace to protect users from trivial errors. role, api_key = role.strip(), api_key.strip() if not is_remote_server(role): try: profile = get_user_profile_by_email(role) # type: Union[UserProfile, RemoteZulipServer] except UserProfile.DoesNotExist: raise JsonableError(_("Invalid user: %s") % (role,)) else: try: profile = get_remote_server_by_uuid(role) except RemoteZulipServer.DoesNotExist: raise JsonableError(_("Invalid Zulip server: %s") % (role,)) if api_key != profile.api_key: if len(api_key) != 32: reason = _("Incorrect API key length (keys should be 32 " "characters long) for role '%s'") else: reason = _("Invalid API key for role '%s'") raise JsonableError(reason % (role,)) # early exit for RemoteZulipServer instances if settings.ZILENCER_ENABLED and isinstance(profile, RemoteZulipServer): if not check_subdomain(get_subdomain(request), ""): raise JsonableError(_("This API key only works on the root subdomain")) return profile profile = cast(UserProfile, profile) # is UserProfile if not profile.is_active: raise JsonableError(_("Account not active")) if profile.is_incoming_webhook and not is_webhook: raise JsonableError(_("Account is not valid to post webhook messages")) if profile.realm.deactivated: raise JsonableError(_("Realm for account has been deactivated")) if (not check_subdomain(get_subdomain(request), profile.realm.subdomain) and # Allow access to localhost for Tornado not (settings.RUNNING_INSIDE_TORNADO and request.META["SERVER_NAME"] == "127.0.0.1" and request.META["REMOTE_ADDR"] == "127.0.0.1")): logging.warning("User %s attempted to access API on wrong subdomain %s" % ( profile.email, get_subdomain(request))) raise JsonableError(_("Account is not associated with this subdomain")) return profile
def encode_credentials(self, identifier: Text, realm: Text="zulip") -> Text: """ identifier: Can be an email or a remote server uuid. """ if identifier in API_KEYS: api_key = API_KEYS[identifier] else: if is_remote_server(identifier): api_key = get_remote_server_by_uuid(identifier).api_key else: api_key = get_user(identifier, get_realm(realm)).api_key API_KEYS[identifier] = api_key credentials = "%s:%s" % (identifier, api_key) return 'Basic ' + base64.b64encode(credentials.encode('utf-8')).decode('utf-8')
def encode_credentials(self, identifier: str, realm: str = "zulip") -> str: """ identifier: Can be an email or a remote server uuid. """ if identifier in self.API_KEYS: api_key = self.API_KEYS[identifier] else: if is_remote_server(identifier): api_key = get_remote_server_by_uuid(identifier).api_key else: user = get_user_by_delivery_email(identifier, get_realm(realm)) api_key = get_api_key(user) self.API_KEYS[identifier] = api_key credentials = "%s:%s" % (identifier, api_key) return 'Basic ' + base64.b64encode( credentials.encode('utf-8')).decode('utf-8')
def api_auth(self, identifier): # type: (Text) -> Dict[str, Text] """ identifier: Can be an email or a remote server uuid. """ if identifier in API_KEYS: api_key = API_KEYS[identifier] else: if is_remote_server(identifier): api_key = get_remote_server_by_uuid(identifier).api_key else: api_key = get_user_profile_by_email(identifier).api_key API_KEYS[identifier] = api_key credentials = u"%s:%s" % (identifier, api_key) return { 'HTTP_AUTHORIZATION': u'Basic ' + base64.b64encode(credentials.encode('utf-8')).decode('utf-8') }
def api_auth(self, identifier): # type: (Text) -> Dict[str, Text] """ identifier: Can be an email or a remote server uuid. """ if identifier in API_KEYS: api_key = API_KEYS[identifier] else: if is_remote_server(identifier): api_key = get_remote_server_by_uuid(identifier).api_key else: api_key = get_user_profile_by_email(identifier).api_key API_KEYS[identifier] = api_key credentials = u"%s:%s" % (identifier, api_key) return { 'HTTP_AUTHORIZATION': u'Basic ' + base64.b64encode(credentials.encode('utf-8')).decode('utf-8') }
def api_auth(self, identifier: Text, realm: Text = "zulip") -> Dict[str, Text]: """ identifier: Can be an email or a remote server uuid. """ if identifier in API_KEYS: api_key = API_KEYS[identifier] else: if is_remote_server(identifier): api_key = get_remote_server_by_uuid(identifier).api_key else: api_key = get_user(identifier, get_realm(realm)).api_key API_KEYS[identifier] = api_key credentials = "%s:%s" % (identifier, api_key) return { 'HTTP_AUTHORIZATION': 'Basic ' + base64.b64encode(credentials.encode('utf-8')).decode('utf-8') }