|
|
@@ -2,6 +2,7 @@ import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
|
|
import { Reflector } from '@nestjs/core';
|
|
|
import { Permission } from '@vendure/common/lib/generated-types';
|
|
|
import { Request, Response } from 'express';
|
|
|
+import { GraphQLResolveInfo } from 'graphql';
|
|
|
|
|
|
import { REQUEST_CONTEXT_KEY } from '../../common/constants';
|
|
|
import { ForbiddenError } from '../../common/error/errors';
|
|
|
@@ -19,8 +20,13 @@ import { setSessionToken } from '../common/set-session-token';
|
|
|
import { PERMISSIONS_METADATA_KEY } from '../decorators/allow.decorator';
|
|
|
|
|
|
/**
|
|
|
- * A guard which checks for the existence of a valid session token in the request and if found,
|
|
|
+ * @description
|
|
|
+ * A guard which:
|
|
|
+ *
|
|
|
+ * 1. checks for the existence of a valid session token in the request and if found,
|
|
|
* attaches the current User entity to the request.
|
|
|
+ * 2. enforces any permissions required by the target handler (resolver, field resolver or route),
|
|
|
+ * and throws a ForbiddenError if those permissions are not present.
|
|
|
*/
|
|
|
@Injectable()
|
|
|
export class AuthGuard implements CanActivate {
|
|
|
@@ -37,23 +43,38 @@ export class AuthGuard implements CanActivate {
|
|
|
|
|
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
|
const { req, res, info } = parseContext(context);
|
|
|
- const authDisabled = this.configService.authOptions.disableAuth;
|
|
|
+ const isFieldResolver = this.isFieldResolver(info);
|
|
|
const permissions = this.reflector.get<Permission[]>(PERMISSIONS_METADATA_KEY, context.getHandler());
|
|
|
+ if (isFieldResolver && !permissions) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ const authDisabled = this.configService.authOptions.disableAuth;
|
|
|
const isPublic = !!permissions && permissions.includes(Permission.Public);
|
|
|
const hasOwnerPermission = !!permissions && permissions.includes(Permission.Owner);
|
|
|
- const session = await this.getSession(req, res, hasOwnerPermission);
|
|
|
- let requestContext = await this.requestContextService.fromRequest(req, info, permissions, session);
|
|
|
-
|
|
|
- const requestContextShouldBeReinitialized = await this.setActiveChannel(requestContext, session);
|
|
|
- if (requestContextShouldBeReinitialized) {
|
|
|
+ let requestContext: RequestContext;
|
|
|
+ if (isFieldResolver) {
|
|
|
+ requestContext = (req as any)[REQUEST_CONTEXT_KEY];
|
|
|
+ } else {
|
|
|
+ const session = await this.getSession(req, res, hasOwnerPermission);
|
|
|
requestContext = await this.requestContextService.fromRequest(req, info, permissions, session);
|
|
|
+
|
|
|
+ const requestContextShouldBeReinitialized = await this.setActiveChannel(requestContext, session);
|
|
|
+ if (requestContextShouldBeReinitialized) {
|
|
|
+ requestContext = await this.requestContextService.fromRequest(
|
|
|
+ req,
|
|
|
+ info,
|
|
|
+ permissions,
|
|
|
+ session,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ (req as any)[REQUEST_CONTEXT_KEY] = requestContext;
|
|
|
}
|
|
|
- (req as any)[REQUEST_CONTEXT_KEY] = requestContext;
|
|
|
|
|
|
if (authDisabled || !permissions || isPublic) {
|
|
|
return true;
|
|
|
} else {
|
|
|
- const canActivate = requestContext.isAuthorized || requestContext.authorizedAsOwnerOnly;
|
|
|
+ const canActivate =
|
|
|
+ requestContext.userHasPermissions(permissions) || requestContext.authorizedAsOwnerOnly;
|
|
|
if (!canActivate) {
|
|
|
throw new ForbiddenError();
|
|
|
} else {
|
|
|
@@ -129,4 +150,13 @@ export class AuthGuard implements CanActivate {
|
|
|
}
|
|
|
return serializedSession;
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns true is this guard is being called on a FieldResolver, i.e. not a top-level
|
|
|
+ * Query or Mutation resolver.
|
|
|
+ */
|
|
|
+ private isFieldResolver(info?: GraphQLResolveInfo): boolean {
|
|
|
+ const parentType = info?.parentType.name;
|
|
|
+ return parentType != null && parentType !== 'Query' && parentType !== 'Mutation';
|
|
|
+ }
|
|
|
}
|