Exemple #1
0
    def execute_graphql_request(self, request, data, query, variables,
                                operation_name):
        """Execute a GraphQL request and return the result

        Args:
            request (HttpRequest): Request object from Django
            data (dict): Parsed content of the body of the request.
            query (dict): GraphQL query
            variables (dict): Optional variables for the GraphQL query
            operation_name (str): GraphQL operation name: query, mutations etc..

        Returns:
            ExecutionResult: Execution result object from GraphQL with response or error message.
        """
        if not query:
            raise HttpError(
                HttpResponseBadRequest("Must provide query string."))

        try:
            backend = self.get_backend(request)
            document = backend.document_from_string(self.graphql_schema, query)
        except Exception as e:
            return ExecutionResult(errors=[e], invalid=True)

        operation_type = document.get_operation_type(operation_name)
        if operation_type and operation_type != "query":
            raise HttpError(
                HttpResponseBadRequest(
                    f"'{operation_type}' is not a supported operation, Only query are supported."
                ))

        try:
            extra_options = {}
            if self.executor:
                # We only include it optionally since
                # executor is not a valid argument in all backends
                extra_options["executor"] = self.executor

            options = {
                "root_value": self.get_root_value(request),
                "variable_values": variables,
                "operation_name": operation_name,
                "context_value": self.get_context(request),
                "middleware": self.get_middleware(request),
            }
            options.update(extra_options)

            operation_type = document.get_operation_type(operation_name)
            return document.execute(**options)
        except Exception as e:
            return ExecutionResult(errors=[e], invalid=True)
Exemple #2
0
    def execute_graphql_request(
        self,
        request,
        data,
        query,
        variables,
        operation_name,
    ):
        if not query:
            raise HttpError(
                HttpResponseBadRequest("Must provide query string."))
        with opentracing.global_tracer().start_active_span(
                "graphql_query") as scope:
            span = scope.span
            span.set_tag(opentracing.tags.COMPONENT, "GraphQL")

            try:
                document = parse(query)
            except GraphQLError as e:
                return ExecutionResult(errors=[e], data=dict(invalid=True))

            if request.method.lower() == "get":
                operation_ast = get_operation_ast(document, operation_name)
                if operation_ast and operation_ast.operation != OperationType.QUERY:
                    raise HttpError(
                        HttpResponseNotAllowed(
                            ["POST"],
                            "Can only perform a {} operation from a POST request."
                            .format(operation_ast.operation.value),
                        ))

            validation_errors = validate(self.schema.graphql_schema, document)
            if validation_errors:
                return ExecutionResult(data=None, errors=validation_errors)

            try:
                with connection.execute_wrapper(tracing_wrapper):
                    return self.schema.execute(
                        source=query,
                        root_value=self.get_root_value(request),
                        variable_values=variables,
                        operation_name=operation_name,
                        context_value=self.get_context(request),
                        middleware=self.get_middleware(request),
                    )
            except GraphQLError as e:
                span.set_tag(opentracing.tags.ERROR, True)
                return ExecutionResult(errors=[e])
Exemple #3
0
 def dispatch(self, request, *args, **kwargs):
     data = self.parse_body(request)
     show_graphiql = self.graphiql and self.can_display_graphiql(
         request, data)
     if self.batch:
         responses = [self.get_response(request, entry) for entry in data]
         result = "[{}]".format(",".join(
             [response[0] for response in responses]))
         status_code = (responses and max(
             responses, key=lambda response: response[1])[1] or 200)
     else:
         result, status_code = self.get_response(request, data,
                                                 show_graphiql)
     _res = json.loads(result) if result else None
     if _res and 'errors' in _res and _res['errors'] and len(
             _res['errors']) > 0:
         _error = _res['errors'][0]
         _code = 400 if _error['code'] == 'error' else int(_error['code'])
         e = HttpError(
             HttpResponse(status=_code, content_type='application/json'),
             _error['message'])
         response = e.response
         # response.content = self.json_encode(request, {'errors': [self.format_error(e)]})
         response.content = self.json_encode(request, _res)
         return response
     if result and status_code:
         return HttpResponse(status=status_code,
                             content=result,
                             content_type="application/json")
     else:
         return super().dispatch(request, *args, **kwargs)
Exemple #4
0
    def get_user(self, request):
        token = self.get_bearer_token(request)

        if token is None:
            return models.AnonymousUser()

        if not settings.OIDC_USERINFO_ENDPOINT:
            raise ImproperlyConfigured(
                'Token was provided, but "OIDC_USERINFO_ENDPOINT" is not set.')

        userinfo_method = functools.partial(self.get_userinfo, token=token)
        # token might be too long for key so we use hash sum instead.
        hashsum_token = hashlib.sha256(force_bytes(token)).hexdigest()

        try:
            userinfo = cache.get_or_set(
                f"authentication.userinfo.{hashsum_token}",
                userinfo_method,
                timeout=settings.OIDC_BEARER_TOKEN_REVALIDATION_TIME,
            )
            return models.OIDCUser(token=token, userinfo=userinfo)
        except requests.HTTPError as e:
            try:
                if (e.response.status_code in [401, 403]
                        and settings.OIDC_INTROSPECT_ENDPOINT):
                    introspect_method = functools.partial(
                        self.get_introspection, token=token)
                    introspection = cache.get_or_set(
                        f"authentication.introspect.{hashsum_token}",
                        introspect_method,
                        timeout=settings.OIDC_BEARER_TOKEN_REVALIDATION_TIME,
                    )
                    if "client_id" not in introspection:
                        response = HttpResponse(status=401)
                        raise HttpError(response)
                    return models.OIDCUser(token=token,
                                           introspection=introspection)
                else:
                    raise e
            except requests.HTTPError as internal_exception:
                # convert request error to django http error
                response = HttpResponse()
                response.status_code = internal_exception.response.status_code
                raise HttpError(response, message=str(internal_exception))
Exemple #5
0
    def get_bearer_token(self, request):
        auth = get_authorization_header(request).split()
        header_prefix = "Bearer"

        if not auth:
            return None

        if smart_text(auth[0].lower()) != header_prefix.lower():
            raise HttpError(
                HttpResponseUnauthorized("No Bearer Authorization header"))

        if len(auth) == 1:
            msg = "Invalid Authorization header. No credentials provided"
            raise HttpError(HttpResponseUnauthorized(msg))
        elif len(auth) > 2:
            msg = ("Invalid Authorization header. Credentials string should "
                   "not contain spaces.")
            raise HttpError(HttpResponseUnauthorized(msg))

        return auth[1]
Exemple #6
0
    def dispatch(self, request, *args, **kwargs):
        if request.method.lower() not in ('get', 'post', 'options'):
            raise HttpError(
                HttpResponseNotAllowed(
                    ['GET', 'POST', 'OPTIONS'],
                    'GraphQL only supports GET, POST and OPTIONS requests.'))

        if request.method.lower() == 'options':
            return HttpResponse(status=200,
                                content='',
                                content_type='application/json')

        return super().dispatch(request, *args, **kwargs)
Exemple #7
0
    def dispatch(self, request, *args, **kwargs):
        request_type = request.META.get("CONTENT_TYPE", '')

        # import pdb; pdb.set_trace()
        # if request.method == 'POST':
        #     import pdb; pdb.set_trace()

        # NOTE: need to overwrite inherited dispatch func and handle explicitly if multipart since batch=True incorrectly treats multipart as a batch request
        if "multipart/form-data" in request_type:
            try:
                if request.method.lower() not in ("get", "post"):
                    raise HttpError(
                        HttpResponseNotAllowed(
                            ["GET", "POST"], "GraphQL only supports GET and POST requests."
                        )
                    )

                data = self.parse_body(request)
                show_graphiql = self.graphiql and self.can_display_graphiql(request, data)

                result, status_code = self.get_response(request, data, show_graphiql)

                if show_graphiql:
                    query, variables, operation_name, id = self.get_graphql_params(
                        request, data
                    )

                    return self.render_graphiql(
                        request,
                        graphiql_version=self.graphiql_version,
                        query=query or "",
                        variables=json.dumps(variables) or "",
                        operation_name=operation_name or "",
                        result=result or "",
                    )

                return HttpResponse(
                    status=status_code, content=result, content_type="application/json"
                )

            except HttpError as e:
                response = e.response
                response["Content-Type"] = "application/json"
                response.content = self.json_encode(
                    request, {"errors": [self.format_error(e)]}
                )
                return response

        else:
            return super(MyGraphQLView, MyGraphQLView).dispatch(self, request, *args, **kwargs)
Exemple #8
0
    def execute_graphql_request(self,
                                request,
                                data,
                                query,
                                variables,
                                operation_name,
                                show_graphiql=False):
        if isinstance(data, JsonResponse):
            if data.status_code == 403:
                raise HttpError(HttpResponseForbidden(data.content))

        return super(AuthenticatedGraphQLView,
                     self).execute_graphql_request(request, data, query,
                                                   variables, operation_name,
                                                   show_graphiql)
Exemple #9
0
    def dispatch(self, request, *args, **kwargs):
        """Dispatch method to  allow OPTIONS requests.

        This fixes Apollo's graphql client hanging on OPTIONS requests.
        """
        if request.method.lower() not in ("get", "post", "options"):
            raise HttpError(
                HttpResponseNotAllowed(
                    ["GET", "POST", "OPTIONS"],
                    "GraphQL only supports GET, POST and OPTIONS requests.",
                )
            )

        if request.method.lower() == "options":
            return HttpResponse(status=200, content="", content_type="application/json")

        return super().dispatch(request, *args, **kwargs)
Exemple #10
0
    def parse_body(self, request):
        """Analyze the request and based on the content type,
        extract the query from the body as a string or as a JSON payload.

        Args:
            request (HttpRequest): Request object from Django

        Returns:
            dict: GraphQL query
        """
        content_type = GraphQLView.get_content_type(request)

        if content_type == "application/graphql":
            return {"query": request.body.decode()}

        elif content_type == "application/json":
            try:
                return request.data
            except ParseError:
                raise HttpError(HttpResponseBadRequest("Request body contained Invalid JSON."))

        return {}
Exemple #11
0
    def execute_graphql_request(self,
                                request,
                                data,
                                query,
                                variables,
                                operation_name,
                                show_graphiql=False):
        """
        Largely a copy of GraphQLView execute_graphql_request function, the check has to happen in the middle of its
        processing so whole function needs to be replicated here
        """
        if not query:
            if show_graphiql:
                return None
            raise HttpError(
                HttpResponseBadRequest("Must provide query string."))

        try:
            backend = self.get_backend(request)
            document = backend.document_from_string(self.schema, query)
        except Exception as e:
            return ExecutionResult(errors=[e], invalid=True)

        if request.method.lower() == "get":
            operation_type = document.get_operation_type(operation_name)
            if operation_type and operation_type != "query":
                if show_graphiql:
                    return None

                raise HttpError(
                    HttpResponseNotAllowed(
                        ["POST"],
                        "Can only perform a {} operation from a POST request.".
                        format(operation_type),
                    ))

        # Check request weight
        try:
            if self.list_limit or self.weight_limit or self.depth_limit:
                if document:
                    fragments = get_fragments(
                        document.document_ast.definitions)
                    definitions_total_weight = 0
                    for definition in document.document_ast.definitions:
                        if not isinstance(definition, OperationDefinition):
                            continue

                        if operation_name and definition.name != operation_name:
                            continue

                        def_weight = self.calculate_action_weight(
                            definition.selection_set, fragments)
                        definitions_total_weight += def_weight
                        if self.weight_limit and definitions_total_weight > self.weight_limit:
                            raise QueryWeightExceeded(
                                "Your query exceeds the maximum query weight allowed"
                            )
        except Exception as e:
            return ExecutionResult(errors=[e], invalid=True)

        try:
            extra_options = {}
            if self.executor:
                # We only include it optionally since
                # executor is not a valid argument in all backends
                extra_options["executor"] = self.executor

            return document.execute(root_value=self.get_root_value(request),
                                    variable_values=variables,
                                    operation_name=operation_name,
                                    context_value=self.get_context(request),
                                    middleware=self.get_middleware(request),
                                    **extra_options)
        except Exception as e:
            return ExecutionResult(errors=[e], invalid=True)
Exemple #12
0
    def execute_graphql_request(self,
                                request,
                                data,
                                query,
                                variables,
                                operation_name,
                                show_graphiql=False):
        if not query:
            if show_graphiql:
                return None
            raise HttpError(
                HttpResponseBadRequest("Must provide query string."))

        try:
            backend = self.get_backend(request)
            with tracer.trace(op="backend.document_from_string"):
                document = backend.document_from_string(self.schema, query)
        except Exception as e:
            return ExecutionResult(errors=[e], invalid=True)

        if request.method.lower() == "get":
            operation_type = document.get_operation_type(operation_name)
            if operation_type and operation_type != "query":
                if show_graphiql:
                    return None

                raise HttpError(
                    HttpResponseNotAllowed(
                        ["POST"],
                        "Can only perform a {} operation from a POST request.".
                        format(operation_type),
                    ))

        try:
            extra_options = {}
            if self.executor:
                # We only include it optionally since
                # executor is not a valid argument in all backends
                extra_options["executor"] = self.executor

            options = {
                "root_value": self.get_root_value(request),
                "variable_values": variables,
                "operation_name": operation_name,
                "context_value": self.get_context(request),
                "middleware": self.get_middleware(request),
            }
            options.update(extra_options)

            operation_type = document.get_operation_type(operation_name)
            if operation_type == "mutation" and (
                    graphene_settings.ATOMIC_MUTATIONS is True
                    or connection.settings_dict.get("ATOMIC_MUTATIONS",
                                                    False) is True):
                with transaction.atomic():
                    result = document.execute(**options)
                    if getattr(request, MUTATION_ERRORS_FLAG, False) is True:
                        transaction.set_rollback(True)
                return result
            with tracer.trace(op="document.execute"):
                return document.execute(**options)
        except Exception as e:
            return ExecutionResult(errors=[e], invalid=True)