简体   繁体   English

如何在 NestJS 和 GraphQL 中添加 Wrap 解析器以检查 header 中的 email 是否等于查询中的 email

[英]How to add Wrap resolver in NestJS and GraphQL to check if email from header is equal to the email in query

I'm using cognito authentication, I create a middlware我正在使用认知身份验证,我创建了一个中间件

const { email } = payload;
req.headers['user-email'] = email as string;

I want to write this kind of function我想写这样的 function

 public async httpCheck(query: any, args: any, context: any, 
resolveInfo: any) {
console.log('authhealth');
console.log("context "+ context.userEmail);
console.log("query : "+ query.userEmail);
(context.userEmail === query.userEmail ) ? console.log("authorized successfully") : console.log("authorization failed"); 
return 'OK';

} }

This is my file structure, I want to write wrap resolver这是我的文件结构,我想写包装解析器

在此处输入图像描述

From your example, it looks like you are wanting to reject the whole request if the email from the request header does not match an email being provided as an argument to a field in the GraphQL query.从您的示例来看,如果来自请求 header 的 email 与作为参数提供给 GraphQL 查询中的字段的 email 不匹配,您似乎想要拒绝整个请求。

So given the following query:因此给出以下查询:

query MyQuery($userEmail:String!) {
  userByEmail(email: $userEmail) {
    id
    email
    familyName
    givenName
  }
}

If you want to check that the header email equals the email argument of userByEmail BEFORE Postgraphile executes the operation, you need to use a Postgraphile Server Plugin which adds a dynamic validation rule that implements the check:如果你想在 Postgraphile 执行操作之前检查userByEmail email 等于 userByEmail 的email参数,你需要使用一个Postgraphile 服务器插件,它添加了一个动态验证规则来实现检查:

import type { PostGraphilePlugin } from "postgraphile";
import type { ValidationRule } from "graphql";
import { GraphQLError } from "graphql";
import type { IncomingMessage } from "http";
import type { Plugin } from "graphile-build";

// Defines a graphile plugin that uses a field argument build hook to add
// metadata as an extension to the "email" argument of the "userByEmail" field
const AddEmailMatchPlugin: Plugin = (builder) => {
  builder.hook(
    "GraphQLObjectType:fields:field:args",
    (args, build, context) => {
      // access whatever data you need from the field context. The scope contains
      // basically any information you might desire including the database metadata
      // e.g table name, primary key.
      const {
        scope: { fieldName, isRootQuery },
      } = context;

      if (!isRootQuery && fieldName !== "userByEmail") {
        return args;
      }

      if (args.email) {
        return {
          ...args,
          email: {
            ...args.email,
            // add an extensions object to the email argument
            // this will be accessible from the finalized GraphQLSchema object
            extensions: {
              // include any existing extension data
              ...args.email.extensions,
              // this can be whatetever you want, but it's best to create
              // an object using a consistent key for any
              // GraphQL fields/types/args that you modify
              myApp: {
                matchToUserEmail: true,
              },
            },
          },
        };
      }

      return args;
    }
  );
};

// define the server plugin
const matchRequestorEmailWithEmailArgPlugin: PostGraphilePlugin = {
  // this hook enables the addition of dynamic validation rules
  // where we can access the underlying http request
  "postgraphile:validationRules": (
    rules,
    context: { req: IncomingMessage; variables?: Record<string, unknown> }
  ) => {
    const {
      variables,
      // get your custom user context/jwt/headers from the request object
      // this example assumes you've done this in some upstream middleware
      req: { reqUser },
    } = context;

    if (!reqUser) {
      throw Error("No user found!");
    }

    const { email, role } = reqUser;

    const vr: ValidationRule = (validationContext) => {
      return {
        Argument: {
          // this fires when an argument node has been found in query AST
          enter(node, key) {
            if (typeof key === "number") {
              // get the schema definition of the argument
              const argDef = validationContext.getFieldDef()?.args[key];
              if (argDef?.extensions?.myApp?.matchToUserEmail) {
                // restrict check to a custom role
                if (role === "standard") {
                  const nodeValueKind = node.value.kind;
                  let emailsMatch = false;

                  // basic case
                  if (nodeValueKind === "StringValue") {
                    if (node.value.value === email) {
                      emailsMatch = true;
                    }
                  }
                  // must account for the value being provided by a variable
                  else if (nodeValueKind === "Variable") {
                    const varName = node.value.name.value;
                    if (variables && variables[varName] === email) {
                      emailsMatch = true;
                    }
                  }

                  if (!emailsMatch) {
                    validationContext.reportError(
                      new GraphQLError(
                        `Field "${
                          validationContext.getFieldDef()?.name
                        }" argument "${
                          argDef.name
                        }" must match your user email.`,
                        node
                      )
                    );
                  }
                }
              }
            }
          },
        },
      };
    };
    return [...rules, vr];
  },

  // This hook appends the AddEmailMatchPlugin graphile plugin that
  // this server plugin depends on for its custom extension.
  "postgraphile:options": (options) => {
    return {
      ...options,
      appendPlugins: [...(options.appendPlugins || []), AddEmailMatchPlugin],
    };
  },
};

export default matchRequestorEmailWithEmailArgPlugin;

Then you need to register the server plugin in the Postgraphile middleware options:然后你需要在 Postgraphile 中间件选项中注册服务器插件:

const pluginHook = makePluginHook([MatchRequestorEmailWithEmailArgPlugin]);

const postGraphileMiddleware = postgraphile(databaseUrl, "my_schema", {
  pluginHook,
  // ...
});

If you just want to reject the userByEmail field in the query and don't care about rejecting before any resolution of any other parts of the request occur, you can use the makeWrapResolversPlugin to wrap the resolver and do the check there.如果您只想拒绝查询中的userByEmail字段并且不关心在对请求的任何其他部分进行任何解析之前拒绝,您可以使用makeWrapResolversPlugin来包装解析器并在那里进行检查。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM