From 09eb30c5212687c56d696fe5973a139ec83ef84e Mon Sep 17 00:00:00 2001 From: Michael Bromley Date: Wed, 5 Feb 2020 11:10:39 +0100 Subject: [PATCH] fix(core): Prevent Customers from logging in to admin API Closes #77 --- packages/core/e2e/auth.e2e-spec.ts | 18 +++++++++++++++ .../src/api/resolvers/admin/auth.resolver.ts | 14 +++++++---- .../api/resolvers/base/base-auth.resolver.ts | 23 ++++++++++++++++--- .../api/resolvers/shop/shop-auth.resolver.ts | 14 ++++++----- 4 files changed, 56 insertions(+), 13 deletions(-) diff --git a/packages/core/e2e/auth.e2e-spec.ts b/packages/core/e2e/auth.e2e-spec.ts index d58dc9c2c4..ca76e7295d 100644 --- a/packages/core/e2e/auth.e2e-spec.ts +++ b/packages/core/e2e/auth.e2e-spec.ts @@ -11,6 +11,7 @@ import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-conf import { CreateAdministrator, CreateRole, + GetCustomerList, Me, MutationCreateProductArgs, MutationLoginArgs, @@ -22,6 +23,7 @@ import { CREATE_ADMINISTRATOR, CREATE_PRODUCT, CREATE_ROLE, + GET_CUSTOMER_LIST, GET_PRODUCT_LIST, ME, UPDATE_PRODUCT, @@ -66,6 +68,22 @@ describe('Authorization & permissions', () => { }); }); + describe('Customer user', () => { + let customerEmailAddress: string; + beforeAll(async () => { + await adminClient.asSuperAdmin(); + const { customers } = await adminClient.query(GET_CUSTOMER_LIST); + customerEmailAddress = customers.items[0].emailAddress; + }); + + it( + 'cannot login', + assertThrowsWithMessage(async () => { + await adminClient.asUserWithCredentials(customerEmailAddress, 'test'); + }, 'The credentials did not match. Please check and try again'), + ); + }); + describe('ReadCatalog permission', () => { beforeAll(async () => { await adminClient.asSuperAdmin(); diff --git a/packages/core/src/api/resolvers/admin/auth.resolver.ts b/packages/core/src/api/resolvers/admin/auth.resolver.ts index 9f3eb2a49c..3323616d70 100644 --- a/packages/core/src/api/resolvers/admin/auth.resolver.ts +++ b/packages/core/src/api/resolvers/admin/auth.resolver.ts @@ -3,6 +3,7 @@ import { LoginResult, MutationLoginArgs, Permission } from '@vendure/common/lib/ import { Request, Response } from 'express'; import { ConfigService } from '../../../config/config.service'; +import { AdministratorService } from '../../../service/services/administrator.service'; import { AuthService } from '../../../service/services/auth.service'; import { ChannelService } from '../../../service/services/channel.service'; import { CustomerService } from '../../../service/services/customer.service'; @@ -14,8 +15,13 @@ import { BaseAuthResolver } from '../base/base-auth.resolver'; @Resolver() export class AuthResolver extends BaseAuthResolver { - constructor(authService: AuthService, userService: UserService, configService: ConfigService) { - super(authService, userService, configService); + constructor( + authService: AuthService, + userService: UserService, + configService: ConfigService, + administratorService: AdministratorService, + ) { + super(authService, userService, administratorService, configService); } @Mutation() @@ -26,7 +32,7 @@ export class AuthResolver extends BaseAuthResolver { @Context('req') req: Request, @Context('res') res: Response, ): Promise { - return super.login(args, ctx, req, res); + return super.login(args, ctx, req, res, 'admin'); } @Mutation() @@ -42,6 +48,6 @@ export class AuthResolver extends BaseAuthResolver { @Query() @Allow(Permission.Authenticated, Permission.Owner) me(@Ctx() ctx: RequestContext) { - return super.me(ctx); + return super.me(ctx, 'admin'); } } diff --git a/packages/core/src/api/resolvers/base/base-auth.resolver.ts b/packages/core/src/api/resolvers/base/base-auth.resolver.ts index c0148847fd..d0a85a957f 100644 --- a/packages/core/src/api/resolvers/base/base-auth.resolver.ts +++ b/packages/core/src/api/resolvers/base/base-auth.resolver.ts @@ -7,13 +7,15 @@ import { import { unique } from '@vendure/common/lib/unique'; import { Request, Response } from 'express'; -import { ForbiddenError, InternalServerError } from '../../../common/error/errors'; +import { ForbiddenError, InternalServerError, UnauthorizedError } from '../../../common/error/errors'; import { ConfigService } from '../../../config/config.service'; import { User } from '../../../entity/user/user.entity'; import { getUserChannelsPermissions } from '../../../service/helpers/utils/get-user-channels-permissions'; +import { AdministratorService } from '../../../service/services/administrator.service'; import { AuthService } from '../../../service/services/auth.service'; import { UserService } from '../../../service/services/user.service'; import { extractAuthToken } from '../../common/extract-auth-token'; +import { ApiType } from '../../common/get-api-type'; import { RequestContext } from '../../common/request-context'; import { setAuthToken } from '../../common/set-auth-token'; @@ -21,6 +23,7 @@ export class BaseAuthResolver { constructor( protected authService: AuthService, protected userService: UserService, + protected administratorService: AdministratorService, protected configService: ConfigService, ) {} @@ -33,8 +36,9 @@ export class BaseAuthResolver { ctx: RequestContext, req: Request, res: Response, + apiType: ApiType, ): Promise { - return await this.createAuthenticatedSession(ctx, args, req, res); + return await this.createAuthenticatedSession(ctx, args, req, res, apiType); } async logout(ctx: RequestContext, req: Request, res: Response): Promise { @@ -56,11 +60,17 @@ export class BaseAuthResolver { /** * Returns information about the current authenticated user. */ - async me(ctx: RequestContext) { + async me(ctx: RequestContext, apiType: ApiType) { const userId = ctx.activeUserId; if (!userId) { throw new ForbiddenError(); } + if (apiType === 'admin') { + const administrator = await this.administratorService.findOneByUserId(userId); + if (!administrator) { + throw new ForbiddenError(); + } + } const user = userId && (await this.userService.getUserById(userId)); return user ? this.publiclyAccessibleUser(user) : null; } @@ -73,8 +83,15 @@ export class BaseAuthResolver { args: MutationLoginArgs, req: Request, res: Response, + apiType?: ApiType, ) { const session = await this.authService.authenticate(ctx, args.username, args.password); + if (apiType && apiType === 'admin') { + const administrator = await this.administratorService.findOneByUserId(session.user.id); + if (!administrator) { + throw new UnauthorizedError(); + } + } setAuthToken({ req, res, diff --git a/packages/core/src/api/resolvers/shop/shop-auth.resolver.ts b/packages/core/src/api/resolvers/shop/shop-auth.resolver.ts index 44cb7bcf52..89cc61cf4f 100644 --- a/packages/core/src/api/resolvers/shop/shop-auth.resolver.ts +++ b/packages/core/src/api/resolvers/shop/shop-auth.resolver.ts @@ -20,6 +20,7 @@ import { VerificationTokenError, } from '../../../common/error/errors'; import { ConfigService } from '../../../config/config.service'; +import { AdministratorService } from '../../../service/services/administrator.service'; import { AuthService } from '../../../service/services/auth.service'; import { CustomerService } from '../../../service/services/customer.service'; import { UserService } from '../../../service/services/user.service'; @@ -31,12 +32,13 @@ import { BaseAuthResolver } from '../base/base-auth.resolver'; @Resolver() export class ShopAuthResolver extends BaseAuthResolver { constructor( - protected authService: AuthService, - protected userService: UserService, + authService: AuthService, + userService: UserService, + administratorService: AdministratorService, + configService: ConfigService, protected customerService: CustomerService, - protected configService: ConfigService, ) { - super(authService, userService, configService); + super(authService, userService, administratorService, configService); } @Mutation() @@ -47,7 +49,7 @@ export class ShopAuthResolver extends BaseAuthResolver { @Context('req') req: Request, @Context('res') res: Response, ): Promise { - return super.login(args, ctx, req, res); + return super.login(args, ctx, req, res, 'shop'); } @Mutation() @@ -63,7 +65,7 @@ export class ShopAuthResolver extends BaseAuthResolver { @Query() @Allow(Permission.Authenticated) me(@Ctx() ctx: RequestContext) { - return super.me(ctx); + return super.me(ctx, 'shop'); } @Mutation()