role.e2e-spec.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. /* tslint:disable:no-non-null-assertion */
  2. import { omit } from '@vendure/common/lib/omit';
  3. import {
  4. CUSTOMER_ROLE_CODE,
  5. DEFAULT_CHANNEL_CODE,
  6. SUPER_ADMIN_ROLE_CODE,
  7. } from '@vendure/common/lib/shared-constants';
  8. import { createTestEnvironment, E2E_DEFAULT_CHANNEL_TOKEN } from '@vendure/testing';
  9. import gql from 'graphql-tag';
  10. import path from 'path';
  11. import { initialData } from '../../../e2e-common/e2e-initial-data';
  12. import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
  13. import { ROLE_FRAGMENT } from './graphql/fragments';
  14. import {
  15. ChannelFragment,
  16. CreateAdministratorMutation,
  17. CreateAdministratorMutationVariables,
  18. CreateChannel,
  19. CreateRole,
  20. CreateRoleMutation,
  21. CreateRoleMutationVariables,
  22. CurrencyCode,
  23. DeleteRole,
  24. DeletionResult,
  25. GetChannelsQuery,
  26. GetRole,
  27. GetRoles,
  28. LanguageCode,
  29. Permission,
  30. Role,
  31. UpdateAdministratorMutation,
  32. UpdateAdministratorMutationVariables,
  33. UpdateRole,
  34. UpdateRoleMutation,
  35. UpdateRoleMutationVariables,
  36. } from './graphql/generated-e2e-admin-types';
  37. import {
  38. CREATE_ADMINISTRATOR,
  39. CREATE_CHANNEL,
  40. CREATE_ROLE,
  41. GET_CHANNELS,
  42. UPDATE_ADMINISTRATOR,
  43. UPDATE_ROLE,
  44. } from './graphql/shared-definitions';
  45. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  46. import { sortById } from './utils/test-order-utils';
  47. describe('Role resolver', () => {
  48. const { server, adminClient } = createTestEnvironment(testConfig());
  49. let createdRole: Role.Fragment;
  50. let defaultRoles: Role.Fragment[];
  51. beforeAll(async () => {
  52. await server.init({
  53. initialData,
  54. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
  55. customerCount: 1,
  56. });
  57. await adminClient.asSuperAdmin();
  58. }, TEST_SETUP_TIMEOUT_MS);
  59. afterAll(async () => {
  60. await server.destroy();
  61. });
  62. it('roles', async () => {
  63. const result = await adminClient.query<GetRoles.Query, GetRoles.Variables>(GET_ROLES);
  64. defaultRoles = result.roles.items;
  65. expect(result.roles.items.length).toBe(2);
  66. expect(result.roles.totalItems).toBe(2);
  67. });
  68. it('createRole with invalid permission', async () => {
  69. try {
  70. await adminClient.query<CreateRole.Mutation, CreateRole.Variables>(CREATE_ROLE, {
  71. input: {
  72. code: 'test',
  73. description: 'test role',
  74. permissions: ['ReadCatalogx' as any],
  75. },
  76. });
  77. fail('Should have thrown');
  78. } catch (e) {
  79. expect(e.response.errors[0]?.extensions.code).toBe('BAD_USER_INPUT');
  80. }
  81. });
  82. it('createRole with no permissions includes Authenticated', async () => {
  83. const { createRole } = await adminClient.query<CreateRole.Mutation, CreateRole.Variables>(
  84. CREATE_ROLE,
  85. {
  86. input: {
  87. code: 'test',
  88. description: 'test role',
  89. permissions: [],
  90. },
  91. },
  92. );
  93. expect(omit(createRole, ['channels'])).toEqual({
  94. code: 'test',
  95. description: 'test role',
  96. id: 'T_3',
  97. permissions: [Permission.Authenticated],
  98. });
  99. });
  100. it('createRole deduplicates permissions', async () => {
  101. const { createRole } = await adminClient.query<CreateRole.Mutation, CreateRole.Variables>(
  102. CREATE_ROLE,
  103. {
  104. input: {
  105. code: 'test2',
  106. description: 'test role2',
  107. permissions: [Permission.ReadSettings, Permission.ReadSettings],
  108. },
  109. },
  110. );
  111. expect(omit(createRole, ['channels'])).toEqual({
  112. code: 'test2',
  113. description: 'test role2',
  114. id: 'T_4',
  115. permissions: [Permission.Authenticated, Permission.ReadSettings],
  116. });
  117. });
  118. it('createRole with permissions', async () => {
  119. const result = await adminClient.query<CreateRole.Mutation, CreateRole.Variables>(CREATE_ROLE, {
  120. input: {
  121. code: 'test',
  122. description: 'test role',
  123. permissions: [Permission.ReadCustomer, Permission.UpdateCustomer],
  124. },
  125. });
  126. createdRole = result.createRole;
  127. expect(createdRole).toEqual({
  128. code: 'test',
  129. description: 'test role',
  130. id: 'T_5',
  131. permissions: [Permission.Authenticated, Permission.ReadCustomer, Permission.UpdateCustomer],
  132. channels: [
  133. {
  134. code: DEFAULT_CHANNEL_CODE,
  135. id: 'T_1',
  136. token: 'e2e-default-channel',
  137. },
  138. ],
  139. });
  140. });
  141. it('role', async () => {
  142. const result = await adminClient.query<GetRole.Query, GetRole.Variables>(GET_ROLE, {
  143. id: createdRole.id,
  144. });
  145. expect(result.role).toEqual(createdRole);
  146. });
  147. describe('updateRole', () => {
  148. it('updates role', async () => {
  149. const result = await adminClient.query<UpdateRole.Mutation, UpdateRole.Variables>(UPDATE_ROLE, {
  150. input: {
  151. id: createdRole.id,
  152. code: 'test-modified',
  153. description: 'test role modified',
  154. permissions: [
  155. Permission.ReadCustomer,
  156. Permission.UpdateCustomer,
  157. Permission.DeleteCustomer,
  158. ],
  159. },
  160. });
  161. expect(omit(result.updateRole, ['channels'])).toEqual({
  162. code: 'test-modified',
  163. description: 'test role modified',
  164. id: 'T_5',
  165. permissions: [
  166. Permission.Authenticated,
  167. Permission.ReadCustomer,
  168. Permission.UpdateCustomer,
  169. Permission.DeleteCustomer,
  170. ],
  171. });
  172. });
  173. it('works with partial input', async () => {
  174. const result = await adminClient.query<UpdateRole.Mutation, UpdateRole.Variables>(UPDATE_ROLE, {
  175. input: {
  176. id: createdRole.id,
  177. code: 'test-modified-again',
  178. },
  179. });
  180. expect(result.updateRole.code).toBe('test-modified-again');
  181. expect(result.updateRole.description).toBe('test role modified');
  182. expect(result.updateRole.permissions).toEqual([
  183. Permission.Authenticated,
  184. Permission.ReadCustomer,
  185. Permission.UpdateCustomer,
  186. Permission.DeleteCustomer,
  187. ]);
  188. });
  189. it('deduplicates permissions', async () => {
  190. const result = await adminClient.query<UpdateRole.Mutation, UpdateRole.Variables>(UPDATE_ROLE, {
  191. input: {
  192. id: createdRole.id,
  193. permissions: [
  194. Permission.Authenticated,
  195. Permission.Authenticated,
  196. Permission.ReadCustomer,
  197. Permission.ReadCustomer,
  198. ],
  199. },
  200. });
  201. expect(result.updateRole.permissions).toEqual([
  202. Permission.Authenticated,
  203. Permission.ReadCustomer,
  204. ]);
  205. });
  206. it(
  207. 'does not allow setting non-assignable permissions - Owner',
  208. assertThrowsWithMessage(async () => {
  209. await adminClient.query<UpdateRole.Mutation, UpdateRole.Variables>(UPDATE_ROLE, {
  210. input: {
  211. id: createdRole.id,
  212. permissions: [Permission.Owner],
  213. },
  214. });
  215. }, 'The permission "Owner" may not be assigned'),
  216. );
  217. it(
  218. 'does not allow setting non-assignable permissions - Public',
  219. assertThrowsWithMessage(async () => {
  220. await adminClient.query<UpdateRole.Mutation, UpdateRole.Variables>(UPDATE_ROLE, {
  221. input: {
  222. id: createdRole.id,
  223. permissions: [Permission.Public],
  224. },
  225. });
  226. }, 'The permission "Public" may not be assigned'),
  227. );
  228. it(
  229. 'does not allow setting SuperAdmin permission',
  230. assertThrowsWithMessage(async () => {
  231. await adminClient.query<UpdateRole.Mutation, UpdateRole.Variables>(UPDATE_ROLE, {
  232. input: {
  233. id: createdRole.id,
  234. permissions: [Permission.SuperAdmin],
  235. },
  236. });
  237. }, 'The permission "SuperAdmin" may not be assigned'),
  238. );
  239. it(
  240. 'is not allowed for SuperAdmin role',
  241. assertThrowsWithMessage(async () => {
  242. const superAdminRole = defaultRoles.find(r => r.code === SUPER_ADMIN_ROLE_CODE);
  243. if (!superAdminRole) {
  244. fail(`Could not find SuperAdmin role`);
  245. return;
  246. }
  247. return adminClient.query<UpdateRole.Mutation, UpdateRole.Variables>(UPDATE_ROLE, {
  248. input: {
  249. id: superAdminRole.id,
  250. code: 'superadmin-modified',
  251. description: 'superadmin modified',
  252. permissions: [Permission.Authenticated],
  253. },
  254. });
  255. }, `The role '${SUPER_ADMIN_ROLE_CODE}' cannot be modified`),
  256. );
  257. it(
  258. 'is not allowed for Customer role',
  259. assertThrowsWithMessage(async () => {
  260. const customerRole = defaultRoles.find(r => r.code === CUSTOMER_ROLE_CODE);
  261. if (!customerRole) {
  262. fail(`Could not find Customer role`);
  263. return;
  264. }
  265. return adminClient.query<UpdateRole.Mutation, UpdateRole.Variables>(UPDATE_ROLE, {
  266. input: {
  267. id: customerRole.id,
  268. code: 'customer-modified',
  269. description: 'customer modified',
  270. permissions: [Permission.Authenticated, Permission.DeleteAdministrator],
  271. },
  272. });
  273. }, `The role '${CUSTOMER_ROLE_CODE}' cannot be modified`),
  274. );
  275. });
  276. it(
  277. 'deleteRole is not allowed for Customer role',
  278. assertThrowsWithMessage(async () => {
  279. const customerRole = defaultRoles.find(r => r.code === CUSTOMER_ROLE_CODE);
  280. if (!customerRole) {
  281. fail(`Could not find Customer role`);
  282. return;
  283. }
  284. return adminClient.query<DeleteRole.Mutation, DeleteRole.Variables>(DELETE_ROLE, {
  285. id: customerRole.id,
  286. });
  287. }, `The role '${CUSTOMER_ROLE_CODE}' cannot be deleted`),
  288. );
  289. it(
  290. 'deleteRole is not allowed for SuperAdmin role',
  291. assertThrowsWithMessage(async () => {
  292. const superAdminRole = defaultRoles.find(r => r.code === SUPER_ADMIN_ROLE_CODE);
  293. if (!superAdminRole) {
  294. fail(`Could not find Customer role`);
  295. return;
  296. }
  297. return adminClient.query<DeleteRole.Mutation, DeleteRole.Variables>(DELETE_ROLE, {
  298. id: superAdminRole.id,
  299. });
  300. }, `The role '${SUPER_ADMIN_ROLE_CODE}' cannot be deleted`),
  301. );
  302. it('deleteRole deletes a role', async () => {
  303. const { deleteRole } = await adminClient.query<DeleteRole.Mutation, DeleteRole.Variables>(
  304. DELETE_ROLE,
  305. {
  306. id: createdRole.id,
  307. },
  308. );
  309. expect(deleteRole.result).toBe(DeletionResult.DELETED);
  310. const { role } = await adminClient.query<GetRole.Query, GetRole.Variables>(GET_ROLE, {
  311. id: createdRole.id,
  312. });
  313. expect(role).toBeNull();
  314. });
  315. describe('multi-channel', () => {
  316. let secondChannel: ChannelFragment;
  317. let multiChannelRole: CreateRole.CreateRole;
  318. beforeAll(async () => {
  319. const { createChannel } = await adminClient.query<
  320. CreateChannel.Mutation,
  321. CreateChannel.Variables
  322. >(CREATE_CHANNEL, {
  323. input: {
  324. code: 'second-channel',
  325. token: 'second-channel-token',
  326. defaultLanguageCode: LanguageCode.en,
  327. currencyCode: CurrencyCode.GBP,
  328. pricesIncludeTax: true,
  329. defaultShippingZoneId: 'T_1',
  330. defaultTaxZoneId: 'T_1',
  331. },
  332. });
  333. secondChannel = createChannel as any;
  334. });
  335. it('createRole with specified channel', async () => {
  336. const result = await adminClient.query<CreateRole.Mutation, CreateRole.Variables>(CREATE_ROLE, {
  337. input: {
  338. code: 'multi-test',
  339. description: 'multi channel test role',
  340. permissions: [Permission.ReadCustomer],
  341. channelIds: [secondChannel.id],
  342. },
  343. });
  344. multiChannelRole = result.createRole;
  345. expect(multiChannelRole).toEqual({
  346. code: 'multi-test',
  347. description: 'multi channel test role',
  348. id: 'T_6',
  349. permissions: [Permission.Authenticated, Permission.ReadCustomer],
  350. channels: [
  351. {
  352. code: 'second-channel',
  353. id: 'T_2',
  354. token: 'second-channel-token',
  355. },
  356. ],
  357. });
  358. });
  359. it('updateRole with specified channel', async () => {
  360. const { updateRole } = await adminClient.query<UpdateRole.Mutation, UpdateRole.Variables>(
  361. UPDATE_ROLE,
  362. {
  363. input: {
  364. id: multiChannelRole.id,
  365. channelIds: ['T_1', 'T_2'],
  366. },
  367. },
  368. );
  369. expect(updateRole.channels.sort(sortById)).toEqual([
  370. {
  371. code: DEFAULT_CHANNEL_CODE,
  372. id: 'T_1',
  373. token: 'e2e-default-channel',
  374. },
  375. {
  376. code: 'second-channel',
  377. id: 'T_2',
  378. token: 'second-channel-token',
  379. },
  380. ]);
  381. });
  382. });
  383. // https://github.com/vendure-ecommerce/vendure/issues/1874
  384. describe('role escalation', () => {
  385. let defaultChannel: GetChannelsQuery['channels'][number];
  386. let secondChannel: GetChannelsQuery['channels'][number];
  387. let limitedAdmin: CreateAdministratorMutation['createAdministrator'];
  388. let orderReaderRole: CreateRoleMutation['createRole'];
  389. let adminCreatorRole: CreateRoleMutation['createRole'];
  390. let adminCreatorAdministrator: CreateAdministratorMutation['createAdministrator'];
  391. beforeAll(async () => {
  392. const { channels } = await adminClient.query<GetChannelsQuery>(GET_CHANNELS);
  393. defaultChannel = channels.find(c => c.token === E2E_DEFAULT_CHANNEL_TOKEN)!;
  394. secondChannel = channels.find(c => c.token !== E2E_DEFAULT_CHANNEL_TOKEN)!;
  395. await adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
  396. await adminClient.asSuperAdmin();
  397. const { createRole } = await adminClient.query<CreateRoleMutation, CreateRoleMutationVariables>(
  398. CREATE_ROLE,
  399. {
  400. input: {
  401. code: 'second-channel-admin-manager',
  402. description: '',
  403. channelIds: [secondChannel.id],
  404. permissions: [
  405. Permission.CreateAdministrator,
  406. Permission.ReadAdministrator,
  407. Permission.UpdateAdministrator,
  408. Permission.DeleteAdministrator,
  409. ],
  410. },
  411. },
  412. );
  413. const { createAdministrator } = await adminClient.query<
  414. CreateAdministratorMutation,
  415. CreateAdministratorMutationVariables
  416. >(CREATE_ADMINISTRATOR, {
  417. input: {
  418. firstName: 'channel2',
  419. lastName: 'admin manager',
  420. emailAddress: 'channel2@test.com',
  421. roleIds: [createRole.id],
  422. password: 'test',
  423. },
  424. });
  425. limitedAdmin = createAdministrator;
  426. const { createRole: createRole2 } = await adminClient.query<
  427. CreateRoleMutation,
  428. CreateRoleMutationVariables
  429. >(CREATE_ROLE, {
  430. input: {
  431. code: 'second-channel-order-manager',
  432. description: '',
  433. channelIds: [secondChannel.id],
  434. permissions: [Permission.ReadOrder],
  435. },
  436. });
  437. orderReaderRole = createRole2;
  438. adminClient.setChannelToken(secondChannel.token);
  439. await adminClient.asUserWithCredentials(limitedAdmin.emailAddress, 'test');
  440. });
  441. it(
  442. 'limited admin cannot create Role with SuperAdmin permission',
  443. assertThrowsWithMessage(async () => {
  444. await adminClient.query<CreateRoleMutation, CreateRoleMutationVariables>(CREATE_ROLE, {
  445. input: {
  446. code: 'evil-superadmin',
  447. description: '',
  448. channelIds: [secondChannel.id],
  449. permissions: [Permission.SuperAdmin],
  450. },
  451. });
  452. }, 'The permission "SuperAdmin" may not be assigned'),
  453. );
  454. it(
  455. 'limited admin cannot create Administrator with SuperAdmin role',
  456. assertThrowsWithMessage(async () => {
  457. const superAdminRole = defaultRoles.find(r => r.code === SUPER_ADMIN_ROLE_CODE)!;
  458. await adminClient.query<CreateAdministratorMutation, CreateAdministratorMutationVariables>(
  459. CREATE_ADMINISTRATOR,
  460. {
  461. input: {
  462. firstName: 'Dr',
  463. lastName: 'Evil',
  464. emailAddress: 'drevil@test.com',
  465. roleIds: [superAdminRole.id],
  466. password: 'test',
  467. },
  468. },
  469. );
  470. }, 'Active user does not have sufficient permissions'),
  471. );
  472. it(
  473. 'limited admin cannot create Role with permissions it itself does not have',
  474. assertThrowsWithMessage(async () => {
  475. await adminClient.query<CreateRoleMutation, CreateRoleMutationVariables>(CREATE_ROLE, {
  476. input: {
  477. code: 'evil-order-manager',
  478. description: '',
  479. channelIds: [secondChannel.id],
  480. permissions: [Permission.ReadOrder],
  481. },
  482. });
  483. }, 'Active user does not have sufficient permissions'),
  484. );
  485. it(
  486. 'limited admin cannot create Role on channel it does not have permissions on',
  487. assertThrowsWithMessage(async () => {
  488. await adminClient.query<CreateRoleMutation, CreateRoleMutationVariables>(CREATE_ROLE, {
  489. input: {
  490. code: 'evil-order-manager',
  491. description: '',
  492. channelIds: [defaultChannel.id],
  493. permissions: [Permission.CreateAdministrator],
  494. },
  495. });
  496. }, 'You are not currently authorized to perform this action'),
  497. );
  498. it(
  499. 'limited admin cannot create Administrator with a Role with greater permissions than they themselves have',
  500. assertThrowsWithMessage(async () => {
  501. await adminClient.query<CreateAdministratorMutation, CreateAdministratorMutationVariables>(
  502. CREATE_ADMINISTRATOR,
  503. {
  504. input: {
  505. firstName: 'Dr',
  506. lastName: 'Evil',
  507. emailAddress: 'drevil@test.com',
  508. roleIds: [orderReaderRole.id],
  509. password: 'test',
  510. },
  511. },
  512. );
  513. }, 'Active user does not have sufficient permissions'),
  514. );
  515. it('limited admin can create Role with permissions it itself has', async () => {
  516. const { createRole } = await adminClient.query<CreateRoleMutation, CreateRoleMutationVariables>(
  517. CREATE_ROLE,
  518. {
  519. input: {
  520. code: 'good-admin-creator',
  521. description: '',
  522. channelIds: [secondChannel.id],
  523. permissions: [Permission.CreateAdministrator],
  524. },
  525. },
  526. );
  527. expect(createRole.code).toBe('good-admin-creator');
  528. adminCreatorRole = createRole;
  529. });
  530. it('limited admin can create Administrator with permissions it itself has', async () => {
  531. const { createAdministrator } = await adminClient.query<
  532. CreateAdministratorMutation,
  533. CreateAdministratorMutationVariables
  534. >(CREATE_ADMINISTRATOR, {
  535. input: {
  536. firstName: 'Admin',
  537. lastName: 'Creator',
  538. emailAddress: 'admincreator@test.com',
  539. roleIds: [adminCreatorRole.id],
  540. password: 'test',
  541. },
  542. });
  543. expect(createAdministrator.emailAddress).toBe('admincreator@test.com');
  544. adminCreatorAdministrator = createAdministrator;
  545. });
  546. it(
  547. 'limited admin cannot update Role with permissions it itself lacks',
  548. assertThrowsWithMessage(async () => {
  549. await adminClient.query<UpdateRoleMutation, UpdateRoleMutationVariables>(UPDATE_ROLE, {
  550. input: {
  551. id: adminCreatorRole.id,
  552. permissions: [Permission.ReadOrder],
  553. },
  554. });
  555. }, 'Active user does not have sufficient permissions'),
  556. );
  557. it(
  558. 'limited admin cannot update Administrator with Role containing permissions it itself lacks',
  559. assertThrowsWithMessage(async () => {
  560. await adminClient.query<UpdateAdministratorMutation, UpdateAdministratorMutationVariables>(
  561. UPDATE_ADMINISTRATOR,
  562. {
  563. input: {
  564. id: adminCreatorAdministrator.id,
  565. roleIds: [adminCreatorRole.id, orderReaderRole.id],
  566. },
  567. },
  568. );
  569. }, 'Active user does not have sufficient permissions'),
  570. );
  571. });
  572. });
  573. export const GET_ROLES = gql`
  574. query GetRoles($options: RoleListOptions) {
  575. roles(options: $options) {
  576. items {
  577. ...Role
  578. }
  579. totalItems
  580. }
  581. }
  582. ${ROLE_FRAGMENT}
  583. `;
  584. export const GET_ROLE = gql`
  585. query GetRole($id: ID!) {
  586. role(id: $id) {
  587. ...Role
  588. }
  589. }
  590. ${ROLE_FRAGMENT}
  591. `;
  592. export const DELETE_ROLE = gql`
  593. mutation DeleteRole($id: ID!) {
  594. deleteRole(id: $id) {
  595. result
  596. message
  597. }
  598. }
  599. `;