Sfoglia il codice sorgente

test(core): Add e2e tests for custom permissions

Relates to #450
Michael Bromley 5 anni fa
parent
commit
f540f435dc

+ 2 - 2
packages/core/e2e/country.e2e-spec.ts

@@ -3,7 +3,7 @@ import gql from 'graphql-tag';
 import path from 'path';
 import path from 'path';
 
 
 import { initialData } from '../../../e2e-common/e2e-initial-data';
 import { initialData } from '../../../e2e-common/e2e-initial-data';
-import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
+import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
 
 
 import { COUNTRY_FRAGMENT } from './graphql/fragments';
 import { COUNTRY_FRAGMENT } from './graphql/fragments';
 import {
 import {
@@ -19,7 +19,7 @@ import { GET_COUNTRY_LIST, UPDATE_COUNTRY } from './graphql/shared-definitions';
 
 
 // tslint:disable:no-non-null-assertion
 // tslint:disable:no-non-null-assertion
 
 
-describe('Facet resolver', () => {
+describe('Country resolver', () => {
     const { server, adminClient } = createTestEnvironment(testConfig);
     const { server, adminClient } = createTestEnvironment(testConfig);
     let countries: GetCountryList.Items[];
     let countries: GetCountryList.Items[];
     let GB: GetCountryList.Items;
     let GB: GetCountryList.Items;

+ 223 - 0
packages/core/e2e/custom-permissions.e2e-spec.ts

@@ -0,0 +1,223 @@
+import { mergeConfig } from '@vendure/common/lib/merge-config';
+import gql from 'graphql-tag';
+import path from 'path';
+
+import { initialData } from '../../../e2e-common/e2e-initial-data';
+import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
+import { createTestEnvironment } from '../../testing/lib/create-test-environment';
+
+import {
+    sync,
+    TestPluginWithCustomPermissions,
+    wishlist,
+} from './fixtures/test-plugins/with-custom-permissions';
+import {
+    AdministratorFragment,
+    CreateAdministrator,
+    CreateRole,
+    RoleFragment,
+    UpdateRole,
+} from './graphql/generated-e2e-admin-types';
+import { CREATE_ADMINISTRATOR, CREATE_ROLE, UPDATE_ROLE } from './graphql/shared-definitions';
+import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
+
+describe('Custom permissions', () => {
+    const { server, adminClient } = createTestEnvironment(
+        mergeConfig(testConfig, {
+            plugins: [TestPluginWithCustomPermissions],
+        }),
+    );
+
+    let testRole: RoleFragment;
+    let testAdmin: AdministratorFragment;
+
+    beforeAll(async () => {
+        await server.init({
+            initialData,
+            productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
+            customerCount: 1,
+        });
+        await adminClient.asSuperAdmin();
+
+        // create a new role and Admin and sign in as that Admin
+        const { createRole } = await adminClient.query<CreateRole.Mutation, CreateRole.Variables>(
+            CREATE_ROLE,
+            {
+                input: {
+                    channelIds: ['T_1'],
+                    code: 'test-role',
+                    description: 'Testing custom permissions',
+                    permissions: [],
+                },
+            },
+        );
+        testRole = createRole;
+        const { createAdministrator } = await adminClient.query<
+            CreateAdministrator.Mutation,
+            CreateAdministrator.Variables
+        >(CREATE_ADMINISTRATOR, {
+            input: {
+                firstName: 'Test',
+                lastName: 'Admin',
+                emailAddress: 'test@admin.com',
+                password: 'test',
+                roleIds: [testRole.id],
+            },
+        });
+
+        testAdmin = createAdministrator;
+    }, TEST_SETUP_TIMEOUT_MS);
+
+    afterAll(async () => {
+        await server.destroy();
+    });
+
+    describe('superadmin has custom permissions automatically', () => {
+        beforeAll(async () => {
+            await adminClient.asSuperAdmin();
+        });
+
+        it('single permission', async () => {
+            const { syncWishlist } = await adminClient.query(SYNC);
+            expect(syncWishlist).toBe(true);
+        });
+
+        it('CRUD create permission', async () => {
+            const { createWishlist } = await adminClient.query(CRUD_CREATE);
+            expect(createWishlist).toBe(true);
+        });
+
+        it('CRUD read permission', async () => {
+            // tslint:disable-next-line:no-shadowed-variable
+            const { wishlist } = await adminClient.query(CRUD_READ);
+            expect(wishlist).toBe(true);
+        });
+
+        it('CRUD update permission', async () => {
+            const { updateWishlist } = await adminClient.query(CRUD_UPDATE);
+            expect(updateWishlist).toBe(true);
+        });
+
+        it('CRUD delete permission', async () => {
+            const { deleteWishlist } = await adminClient.query(CRUD_DELETE);
+            expect(deleteWishlist).toBe(true);
+        });
+    });
+
+    describe('custom permissions prevent unauthorized access', () => {
+        beforeAll(async () => {
+            await adminClient.asUserWithCredentials(testAdmin.emailAddress, 'test');
+        });
+
+        it(
+            'single permission',
+            assertThrowsWithMessage(async () => {
+                await adminClient.query(SYNC);
+            }, 'You are not currently authorized to perform this action'),
+        );
+
+        it(
+            'CRUD create permission',
+            assertThrowsWithMessage(async () => {
+                await adminClient.query(CRUD_CREATE);
+            }, 'You are not currently authorized to perform this action'),
+        );
+
+        it(
+            'CRUD read permission',
+            assertThrowsWithMessage(async () => {
+                await adminClient.query(CRUD_READ);
+            }, 'You are not currently authorized to perform this action'),
+        );
+
+        it(
+            'CRUD update permission',
+            assertThrowsWithMessage(async () => {
+                await adminClient.query(CRUD_UPDATE);
+            }, 'You are not currently authorized to perform this action'),
+        );
+
+        it(
+            'CRUD delete permission',
+            assertThrowsWithMessage(async () => {
+                await adminClient.query(CRUD_DELETE);
+            }, 'You are not currently authorized to perform this action'),
+        );
+    });
+
+    describe('adding permissions enables access', () => {
+        beforeAll(async () => {
+            await adminClient.asSuperAdmin();
+            await adminClient.query<UpdateRole.Mutation, UpdateRole.Variables>(UPDATE_ROLE, {
+                input: {
+                    id: testRole.id,
+                    permissions: [
+                        sync.Permission,
+                        wishlist.Create,
+                        wishlist.Read,
+                        wishlist.Update,
+                        wishlist.Delete,
+                    ],
+                },
+            });
+
+            await adminClient.asUserWithCredentials(testAdmin.emailAddress, 'test');
+        });
+
+        it('single permission', async () => {
+            const { syncWishlist } = await adminClient.query(SYNC);
+            expect(syncWishlist).toBe(true);
+        });
+
+        it('CRUD create permission', async () => {
+            const { createWishlist } = await adminClient.query(CRUD_CREATE);
+            expect(createWishlist).toBe(true);
+        });
+
+        it('CRUD read permission', async () => {
+            // tslint:disable-next-line:no-shadowed-variable
+            const { wishlist } = await adminClient.query(CRUD_READ);
+            expect(wishlist).toBe(true);
+        });
+
+        it('CRUD update permission', async () => {
+            const { updateWishlist } = await adminClient.query(CRUD_UPDATE);
+            expect(updateWishlist).toBe(true);
+        });
+
+        it('CRUD delete permission', async () => {
+            const { deleteWishlist } = await adminClient.query(CRUD_DELETE);
+            expect(deleteWishlist).toBe(true);
+        });
+    });
+});
+
+const SYNC = gql`
+    mutation Sync {
+        syncWishlist
+    }
+`;
+
+const CRUD_READ = gql`
+    query CrudRead {
+        wishlist
+    }
+`;
+
+const CRUD_CREATE = gql`
+    mutation CrudCreate {
+        createWishlist
+    }
+`;
+
+const CRUD_UPDATE = gql`
+    mutation CrudUpdate {
+        updateWishlist
+    }
+`;
+
+const CRUD_DELETE = gql`
+    mutation CrudDelete {
+        deleteWishlist
+    }
+`;

+ 62 - 0
packages/core/e2e/fixtures/test-plugins/with-custom-permissions.ts

@@ -0,0 +1,62 @@
+import { Mutation, Query, Resolver } from '@nestjs/graphql';
+import { LanguageCode } from '@vendure/common/lib/generated-types';
+import { Allow, CrudPermissionDefinition, PermissionDefinition, VendurePlugin } from '@vendure/core';
+import gql from 'graphql-tag';
+
+export const sync = new PermissionDefinition({
+    name: 'SyncWishlists',
+    description: 'Allows syncing wishlists via Admin API',
+});
+export const wishlist = new CrudPermissionDefinition('Wishlist');
+
+@Resolver()
+export class TestWishlistResolver {
+    @Allow(wishlist.Read)
+    @Query()
+    wishlist() {
+        return true;
+    }
+    @Allow(wishlist.Create)
+    @Mutation()
+    createWishlist() {
+        return true;
+    }
+    @Allow(wishlist.Update)
+    @Mutation()
+    updateWishlist() {
+        return true;
+    }
+    @Allow(wishlist.Delete)
+    @Mutation()
+    deleteWishlist() {
+        return true;
+    }
+    @Allow(sync.Permission)
+    @Mutation()
+    syncWishlist() {
+        return true;
+    }
+}
+
+@VendurePlugin({
+    imports: [],
+    adminApiExtensions: {
+        resolvers: [TestWishlistResolver],
+        schema: gql`
+            extend type Query {
+                wishlist: Boolean!
+            }
+            extend type Mutation {
+                createWishlist: Boolean!
+                updateWishlist: Boolean!
+                deleteWishlist: Boolean!
+                syncWishlist: Boolean!
+            }
+        `,
+    },
+    configuration: config => {
+        config.authOptions.customPermissions = [sync, wishlist];
+        return config;
+    },
+})
+export class TestPluginWithCustomPermissions {}

+ 9 - 0
packages/core/e2e/graphql/shared-definitions.ts

@@ -705,3 +705,12 @@ export const UPDATE_GLOBAL_SETTINGS = gql`
     }
     }
     ${GLOBAL_SETTINGS_FRAGMENT}
     ${GLOBAL_SETTINGS_FRAGMENT}
 `;
 `;
+
+export const UPDATE_ROLE = gql`
+    mutation UpdateRole($input: UpdateRoleInput!) {
+        updateRole(input: $input) {
+            ...Role
+        }
+    }
+    ${ROLE_FRAGMENT}
+`;

+ 1 - 10
packages/core/e2e/role.e2e-spec.ts

@@ -26,7 +26,7 @@ import {
     Role,
     Role,
     UpdateRole,
     UpdateRole,
 } from './graphql/generated-e2e-admin-types';
 } from './graphql/generated-e2e-admin-types';
-import { CREATE_CHANNEL, CREATE_ROLE } from './graphql/shared-definitions';
+import { CREATE_CHANNEL, CREATE_ROLE, UPDATE_ROLE } from './graphql/shared-definitions';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
 import { sortById } from './utils/test-order-utils';
 import { sortById } from './utils/test-order-utils';
 
 
@@ -377,15 +377,6 @@ export const GET_ROLE = gql`
     ${ROLE_FRAGMENT}
     ${ROLE_FRAGMENT}
 `;
 `;
 
 
-export const UPDATE_ROLE = gql`
-    mutation UpdateRole($input: UpdateRoleInput!) {
-        updateRole(input: $input) {
-            ...Role
-        }
-    }
-    ${ROLE_FRAGMENT}
-`;
-
 export const DELETE_ROLE = gql`
 export const DELETE_ROLE = gql`
     mutation DeleteRole($id: ID!) {
     mutation DeleteRole($id: ID!) {
         deleteRole(id: $id) {
         deleteRole(id: $id) {

+ 12 - 1
scripts/codegen/generate-graphql-types.ts

@@ -11,9 +11,20 @@ const CLIENT_QUERY_FILES = path.join(
     __dirname,
     __dirname,
     '../../packages/admin-ui/src/lib/core/src/data/definitions/**/*.ts',
     '../../packages/admin-ui/src/lib/core/src/data/definitions/**/*.ts',
 );
 );
+const specFileToIgnore = [
+    'import.e2e-spec',
+    'plugin.e2e-spec',
+    'shop-definitions',
+    'custom-fields.e2e-spec',
+    'price-calculation-strategy.e2e-spec',
+    'list-query-builder.e2e-spec',
+    'shop-order.e2e-spec',
+    'database-transactions.e2e-spec',
+    'custom-permissions.e2e-spec',
+];
 const E2E_ADMIN_QUERY_FILES = path.join(
 const E2E_ADMIN_QUERY_FILES = path.join(
     __dirname,
     __dirname,
-    '../../packages/core/e2e/**/!(import.e2e-spec|plugin.e2e-spec|shop-definitions|custom-fields.e2e-spec|price-calculation-strategy.e2e-spec|list-query-builder.e2e-spec|shop-order.e2e-spec|database-transactions.e2e-spec).ts',
+    `../../packages/core/e2e/**/!(${specFileToIgnore.join('|')}).ts`,
 );
 );
 const E2E_SHOP_QUERY_FILES = [path.join(__dirname, '../../packages/core/e2e/graphql/shop-definitions.ts')];
 const E2E_SHOP_QUERY_FILES = [path.join(__dirname, '../../packages/core/e2e/graphql/shop-definitions.ts')];
 const E2E_ELASTICSEARCH_PLUGIN_QUERY_FILES = path.join(
 const E2E_ELASTICSEARCH_PLUGIN_QUERY_FILES = path.join(