custom-field-permissions.e2e-spec.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. import { mergeConfig } from '@vendure/core';
  2. import { createTestEnvironment, SimpleGraphQLClient } from '@vendure/testing';
  3. import { fail } from 'assert';
  4. import gql from 'graphql-tag';
  5. import path from 'path';
  6. import { afterAll, beforeAll, describe, expect, it } from 'vitest';
  7. import { initialData } from '../../../e2e-common/e2e-initial-data';
  8. import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
  9. import * as Codegen from './graphql/generated-e2e-admin-types';
  10. import { Permission } from './graphql/generated-e2e-shop-types';
  11. import { CREATE_ADMINISTRATOR, CREATE_ROLE, UPDATE_PRODUCT } from './graphql/shared-definitions';
  12. describe('Custom field permissions', () => {
  13. const { server, adminClient, shopClient } = createTestEnvironment(
  14. mergeConfig(testConfig(), {
  15. customFields: {
  16. Product: [
  17. {
  18. name: 'publicField',
  19. type: 'string',
  20. public: true,
  21. defaultValue: 'publicField Value',
  22. },
  23. {
  24. name: 'authenticatedField',
  25. type: 'string',
  26. defaultValue: 'authenticatedField Value',
  27. public: true,
  28. requiresPermission: Permission.Authenticated,
  29. },
  30. {
  31. name: 'updateProductField',
  32. type: 'string',
  33. defaultValue: 'updateProductField Value',
  34. public: true,
  35. requiresPermission: Permission.UpdateProduct,
  36. },
  37. {
  38. name: 'updateProductOrCustomerField',
  39. type: 'string',
  40. defaultValue: 'updateProductOrCustomerField Value',
  41. public: false,
  42. requiresPermission: [Permission.UpdateProduct, Permission.UpdateCustomer],
  43. },
  44. {
  45. name: 'superadminField',
  46. type: 'string',
  47. defaultValue: 'superadminField Value',
  48. public: false,
  49. requiresPermission: Permission.SuperAdmin,
  50. },
  51. ],
  52. },
  53. }),
  54. );
  55. let readProductUpdateProductAdmin: Codegen.CreateAdministratorMutation['createAdministrator'];
  56. let readProductUpdateCustomerAdmin: Codegen.CreateAdministratorMutation['createAdministrator'];
  57. beforeAll(async () => {
  58. await server.init({
  59. initialData,
  60. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  61. customerCount: 3,
  62. });
  63. await adminClient.asSuperAdmin();
  64. readProductUpdateProductAdmin = await createAdminWithPermissions({
  65. adminClient,
  66. name: 'ReadProductUpdateProduct',
  67. permissions: [Permission.ReadProduct, Permission.UpdateProduct],
  68. });
  69. readProductUpdateCustomerAdmin = await createAdminWithPermissions({
  70. adminClient,
  71. name: 'ReadProductUpdateCustomer',
  72. permissions: [Permission.ReadProduct, Permission.UpdateCustomer],
  73. });
  74. }, TEST_SETUP_TIMEOUT_MS);
  75. afterAll(async () => {
  76. await server.destroy();
  77. });
  78. const GET_PRODUCT_WITH_CUSTOM_FIELDS = gql(`
  79. query {
  80. product(id: "T_1") {
  81. id
  82. customFields {
  83. publicField
  84. authenticatedField
  85. updateProductField
  86. updateProductOrCustomerField
  87. superadminField
  88. }
  89. }
  90. }
  91. `);
  92. it('readProductUpdateProductAdmin can read public and updateProduct custom fields', async () => {
  93. await adminClient.asUserWithCredentials(readProductUpdateProductAdmin.emailAddress, 'test');
  94. const { product } = await adminClient.query(GET_PRODUCT_WITH_CUSTOM_FIELDS, {
  95. id: 'T_1',
  96. });
  97. expect(product.customFields).toEqual({
  98. publicField: 'publicField Value',
  99. authenticatedField: 'authenticatedField Value',
  100. updateProductField: 'updateProductField Value',
  101. updateProductOrCustomerField: 'updateProductOrCustomerField Value',
  102. superadminField: null,
  103. });
  104. });
  105. it('readProductUpdateCustomerAdmin can read public and updateCustomer custom fields', async () => {
  106. await adminClient.asUserWithCredentials(readProductUpdateCustomerAdmin.emailAddress, 'test');
  107. const { product } = await adminClient.query(GET_PRODUCT_WITH_CUSTOM_FIELDS, {
  108. id: 'T_1',
  109. });
  110. expect(product.customFields).toEqual({
  111. publicField: 'publicField Value',
  112. authenticatedField: 'authenticatedField Value',
  113. updateProductField: null,
  114. updateProductOrCustomerField: 'updateProductOrCustomerField Value',
  115. superadminField: null,
  116. });
  117. });
  118. it('superadmin can read all custom fields', async () => {
  119. await adminClient.asSuperAdmin();
  120. const { product } = await adminClient.query(GET_PRODUCT_WITH_CUSTOM_FIELDS, {
  121. id: 'T_1',
  122. });
  123. expect(product.customFields).toEqual({
  124. publicField: 'publicField Value',
  125. authenticatedField: 'authenticatedField Value',
  126. updateProductField: 'updateProductField Value',
  127. updateProductOrCustomerField: 'updateProductOrCustomerField Value',
  128. superadminField: 'superadminField Value',
  129. });
  130. });
  131. it('superadmin can update all custom fields', async () => {
  132. await adminClient.asSuperAdmin();
  133. await adminClient.query<Codegen.UpdateProductMutation, Codegen.UpdateProductMutationVariables>(
  134. UPDATE_PRODUCT,
  135. {
  136. input: {
  137. id: 'T_1',
  138. customFields: {
  139. publicField: 'new publicField Value',
  140. authenticatedField: 'new authenticatedField Value',
  141. updateProductField: 'new updateProductField Value',
  142. updateProductOrCustomerField: 'new updateProductOrCustomerField Value',
  143. superadminField: 'new superadminField Value',
  144. },
  145. },
  146. },
  147. );
  148. const { product } = await adminClient.query(GET_PRODUCT_WITH_CUSTOM_FIELDS, {
  149. id: 'T_1',
  150. });
  151. expect(product.customFields).toEqual({
  152. publicField: 'new publicField Value',
  153. authenticatedField: 'new authenticatedField Value',
  154. updateProductField: 'new updateProductField Value',
  155. updateProductOrCustomerField: 'new updateProductOrCustomerField Value',
  156. superadminField: 'new superadminField Value',
  157. });
  158. });
  159. it('readProductUpdateProductAdmin can update updateProduct custom field', async () => {
  160. await adminClient.asUserWithCredentials(readProductUpdateProductAdmin.emailAddress, 'test');
  161. await adminClient.query<Codegen.UpdateProductMutation, Codegen.UpdateProductMutationVariables>(
  162. UPDATE_PRODUCT,
  163. {
  164. input: {
  165. id: 'T_1',
  166. customFields: {
  167. updateProductField: 'new updateProductField Value 2',
  168. },
  169. },
  170. },
  171. );
  172. const { product } = await adminClient.query(GET_PRODUCT_WITH_CUSTOM_FIELDS, {
  173. id: 'T_1',
  174. });
  175. expect(product.customFields.updateProductField).toBe('new updateProductField Value 2');
  176. });
  177. it('readProductUpdateProductAdmin cannot update superadminField', async () => {
  178. await adminClient.asUserWithCredentials(readProductUpdateProductAdmin.emailAddress, 'test');
  179. try {
  180. const result = await adminClient.query<
  181. Codegen.UpdateProductMutation,
  182. Codegen.UpdateProductMutationVariables
  183. >(UPDATE_PRODUCT, {
  184. input: {
  185. id: 'T_1',
  186. customFields: {
  187. superadminField: 'new superadminField Value 2',
  188. },
  189. },
  190. });
  191. fail('Should have thrown');
  192. } catch (e: any) {
  193. expect(e.message).toBe(
  194. 'You do not have the required permissions to update the "superadminField" field',
  195. );
  196. }
  197. });
  198. // This will throw anyway because the user does not have permission to even
  199. // update the Product at all.
  200. it('readProductUpdateCustomerAdmin cannot update updateProductField', async () => {
  201. await adminClient.asUserWithCredentials(readProductUpdateCustomerAdmin.emailAddress, 'test');
  202. try {
  203. const result = await adminClient.query<
  204. Codegen.UpdateProductMutation,
  205. Codegen.UpdateProductMutationVariables
  206. >(UPDATE_PRODUCT, {
  207. input: {
  208. id: 'T_1',
  209. customFields: {
  210. updateProductField: 'new updateProductField Value 2',
  211. },
  212. },
  213. });
  214. fail('Should have thrown');
  215. } catch (e: any) {
  216. expect(e.message).toBe('You are not currently authorized to perform this action');
  217. }
  218. });
  219. describe('Shop API', () => {
  220. const GET_PRODUCT_WITH_PUBLIC_CUSTOM_FIELDS = gql(`
  221. query {
  222. product(id: "T_1") {
  223. id
  224. customFields {
  225. publicField
  226. authenticatedField
  227. updateProductField
  228. }
  229. }
  230. }
  231. `);
  232. it('all public fields are accessible in Shop API regardless of permissions', async () => {
  233. await shopClient.asAnonymousUser();
  234. const { product } = await shopClient.query(GET_PRODUCT_WITH_PUBLIC_CUSTOM_FIELDS, {
  235. id: 'T_1',
  236. });
  237. expect(product.customFields).toEqual({
  238. publicField: 'new publicField Value',
  239. authenticatedField: 'new authenticatedField Value',
  240. updateProductField: 'new updateProductField Value 2',
  241. });
  242. });
  243. });
  244. });
  245. async function createAdminWithPermissions(input: {
  246. adminClient: SimpleGraphQLClient;
  247. name: string;
  248. permissions: Permission[];
  249. }) {
  250. const { adminClient, name, permissions } = input;
  251. const { createRole } = await adminClient.query<
  252. Codegen.CreateRoleMutation,
  253. Codegen.CreateRoleMutationVariables
  254. >(CREATE_ROLE, {
  255. input: {
  256. code: name,
  257. description: name,
  258. permissions,
  259. },
  260. });
  261. const { createAdministrator } = await adminClient.query<
  262. Codegen.CreateAdministratorMutation,
  263. Codegen.CreateAdministratorMutationVariables
  264. >(CREATE_ADMINISTRATOR, {
  265. input: {
  266. firstName: name,
  267. lastName: 'LastName',
  268. emailAddress: `${name}@test.com`,
  269. roleIds: [createRole.id],
  270. password: 'test',
  271. },
  272. });
  273. return createAdministrator;
  274. }