Exemplo n.º 1
0
        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)
Exemplo n.º 2
0
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
Exemplo n.º 3
0
        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)
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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
Exemplo n.º 6
0
    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')
        }
Exemplo n.º 7
0
    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')
        }
Exemplo n.º 8
0
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
Exemplo n.º 9
0
    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')
Exemplo n.º 10
0
    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')
Exemplo n.º 11
0
    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')
        }
Exemplo n.º 12
0
    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')
        }
Exemplo n.º 13
0
    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')
        }