user-groups.test.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. import type { IGrantedGroup } from '@growi/core';
  2. import { GroupType, getIdForRef, type IPage, PageGrant } from '@growi/core';
  3. import mongoose from 'mongoose';
  4. import { PageActionOnGroupDelete } from '../../../src/interfaces/user-group';
  5. import type { PageDocument, PageModel } from '../../../src/server/models/page';
  6. import UserGroup from '../../../src/server/models/user-group';
  7. import UserGroupRelation from '../../../src/server/models/user-group-relation';
  8. import type { IUserGroupService } from '../../../src/server/service/user-group';
  9. import { getInstance } from '../setup-crowi';
  10. describe('UserGroupService', () => {
  11. let crowi;
  12. let User;
  13. let Page: PageModel;
  14. let userGroupService: IUserGroupService;
  15. const groupId1 = new mongoose.Types.ObjectId();
  16. const groupId2 = new mongoose.Types.ObjectId();
  17. const groupId3 = new mongoose.Types.ObjectId();
  18. const groupId4 = new mongoose.Types.ObjectId();
  19. const groupId5 = new mongoose.Types.ObjectId();
  20. const groupId6 = new mongoose.Types.ObjectId();
  21. const groupId7 = new mongoose.Types.ObjectId();
  22. const groupId8 = new mongoose.Types.ObjectId();
  23. const groupId9 = new mongoose.Types.ObjectId();
  24. const groupId10 = new mongoose.Types.ObjectId();
  25. const groupId11 = new mongoose.Types.ObjectId();
  26. const groupId12 = new mongoose.Types.ObjectId();
  27. const groupId13 = new mongoose.Types.ObjectId();
  28. const groupId14 = new mongoose.Types.ObjectId();
  29. const groupId15 = new mongoose.Types.ObjectId();
  30. const userId1 = new mongoose.Types.ObjectId();
  31. let user1;
  32. const pageId1 = new mongoose.Types.ObjectId();
  33. const pageId2 = new mongoose.Types.ObjectId();
  34. let rootPage: PageDocument | null;
  35. // normalize for result comparison
  36. const normalizeGrantedGroups = (
  37. grantedGroups: IGrantedGroup[] | undefined,
  38. ) => {
  39. if (grantedGroups == null) {
  40. return null;
  41. }
  42. return grantedGroups.map((group) => {
  43. return { item: getIdForRef(group.item), type: group.type };
  44. });
  45. };
  46. beforeAll(async () => {
  47. crowi = await getInstance();
  48. User = mongoose.model('User');
  49. Page = mongoose.model<IPage, PageModel>('Page');
  50. rootPage = await Page.findOne({ path: '/' });
  51. userGroupService = crowi.userGroupService;
  52. await User.insertMany([
  53. // ug -> User Group
  54. {
  55. _id: userId1,
  56. name: 'ug_test_user1',
  57. username: 'ug_test_user1',
  58. email: 'ug_test_user1@example.com',
  59. },
  60. ]);
  61. user1 = await User.findOne({ _id: userId1 });
  62. // Create Groups
  63. await UserGroup.insertMany([
  64. // No parent
  65. {
  66. _id: groupId1,
  67. name: 'v5_group1',
  68. description: 'description1',
  69. },
  70. // No parent
  71. {
  72. _id: groupId2,
  73. name: 'v5_group2',
  74. description: 'description2',
  75. },
  76. // No parent
  77. {
  78. _id: groupId3,
  79. name: 'v5_group3',
  80. description: 'description3',
  81. },
  82. // No parent
  83. {
  84. _id: groupId4,
  85. name: 'v5_group4',
  86. description: 'description4',
  87. },
  88. // No parent
  89. {
  90. _id: groupId5,
  91. name: 'v5_group5',
  92. description: 'description5',
  93. },
  94. // No parent
  95. {
  96. _id: groupId6,
  97. name: 'v5_group6',
  98. description: 'description6',
  99. },
  100. // No parent
  101. {
  102. _id: groupId7,
  103. name: 'v5_group7',
  104. description: 'description7',
  105. parent: groupId6,
  106. },
  107. // No parent
  108. {
  109. _id: groupId8,
  110. name: 'v5_group8',
  111. description: 'description8',
  112. },
  113. {
  114. _id: groupId9,
  115. name: 'v5_group9',
  116. description: 'description9',
  117. },
  118. {
  119. _id: groupId10,
  120. name: 'v5_group10',
  121. description: 'description10',
  122. parent: groupId9,
  123. },
  124. {
  125. _id: groupId11,
  126. name: 'v5_group11',
  127. description: 'descriptio11',
  128. },
  129. {
  130. _id: groupId12,
  131. name: 'v5_group12',
  132. description: 'description12',
  133. parent: groupId11,
  134. },
  135. // for removeCompletelyByRootGroupId test
  136. {
  137. _id: groupId13,
  138. name: 'v5_group13',
  139. description: 'description13',
  140. },
  141. {
  142. _id: groupId14,
  143. name: 'v5_group14',
  144. description: 'description14',
  145. parent: groupId13,
  146. },
  147. {
  148. _id: groupId15,
  149. name: 'v5_group15',
  150. description: 'description15',
  151. parent: groupId15,
  152. },
  153. ]);
  154. // Create UserGroupRelations
  155. await UserGroupRelation.insertMany([
  156. {
  157. relatedGroup: groupId4,
  158. relatedUser: userId1,
  159. },
  160. {
  161. relatedGroup: groupId6,
  162. relatedUser: userId1,
  163. },
  164. {
  165. relatedGroup: groupId8,
  166. relatedUser: userId1,
  167. },
  168. {
  169. relatedGroup: groupId9,
  170. relatedUser: userId1,
  171. },
  172. {
  173. relatedGroup: groupId10,
  174. relatedUser: userId1,
  175. },
  176. {
  177. relatedGroup: groupId11,
  178. relatedUser: userId1,
  179. },
  180. {
  181. relatedGroup: groupId12,
  182. relatedUser: userId1,
  183. },
  184. ]);
  185. await Page.insertMany([
  186. {
  187. _id: pageId1,
  188. path: '/canBePublicized',
  189. grant: PageGrant.GRANT_USER_GROUP,
  190. creator: userId1,
  191. lastUpdateUser: userId1,
  192. grantedGroups: [
  193. { item: groupId13, type: GroupType.userGroup },
  194. { item: groupId14, type: GroupType.userGroup },
  195. ],
  196. parent: rootPage?._id,
  197. },
  198. {
  199. _id: pageId2,
  200. path: '/cannotBePublicized',
  201. grant: PageGrant.GRANT_USER_GROUP,
  202. creator: userId1,
  203. lastUpdateUser: userId1,
  204. grantedGroups: [
  205. { item: groupId13, type: GroupType.userGroup },
  206. { item: groupId15, type: GroupType.userGroup },
  207. ],
  208. parent: rootPage?._id,
  209. },
  210. ]);
  211. });
  212. /*
  213. * Update UserGroup
  214. */
  215. describe('updateGroup', () => {
  216. test('Updated values should be reflected. (name, description, parent)', async () => {
  217. const userGroup2 = await UserGroup.findOne({ _id: groupId2 });
  218. const newGroupName = 'v5_group1_new';
  219. const newGroupDescription = 'description1_new';
  220. const newParentId = userGroup2?._id;
  221. const updatedUserGroup = await userGroupService.updateGroup(
  222. groupId1,
  223. newGroupName,
  224. newGroupDescription,
  225. newParentId,
  226. );
  227. expect(updatedUserGroup.name).toBe(newGroupName);
  228. expect(updatedUserGroup.description).toBe(newGroupDescription);
  229. expect(updatedUserGroup.parent).toStrictEqual(newParentId);
  230. });
  231. test('Should throw an error when trying to set existing group name', async () => {
  232. const userGroup2 = await UserGroup.findOne({ _id: groupId2 });
  233. const result = userGroupService.updateGroup(groupId1, userGroup2?.name);
  234. await expect(result).rejects.toThrow('The group name is already taken');
  235. });
  236. test('Parent should be null when parent group is released', async () => {
  237. const userGroup = await UserGroup.findOne({ _id: groupId3 });
  238. const updatedUserGroup = await userGroupService.updateGroup(
  239. userGroup?._id,
  240. userGroup?.name,
  241. userGroup?.description,
  242. null,
  243. );
  244. expect(updatedUserGroup.parent).toBeNull();
  245. });
  246. /*
  247. * forceUpdateParents: false
  248. */
  249. test('Should throw an error when users in child group do not exist in parent group', async () => {
  250. const userGroup4 = await UserGroup.findOne({
  251. _id: groupId4,
  252. parent: null,
  253. });
  254. const result = userGroupService.updateGroup(
  255. userGroup4?._id,
  256. userGroup4?.name,
  257. userGroup4?.description,
  258. groupId5,
  259. );
  260. await expect(result).rejects.toThrow(
  261. 'The parent group does not contain the users in this group.',
  262. );
  263. });
  264. /*
  265. * forceUpdateParents: true
  266. */
  267. test('User should be included to parent group (2 groups ver)', async () => {
  268. const userGroup4 = await UserGroup.findOne({
  269. _id: groupId4,
  270. parent: null,
  271. });
  272. const userGroup5 = await UserGroup.findOne({
  273. _id: groupId5,
  274. parent: null,
  275. });
  276. // userGroup4 has userId1
  277. const userGroupRelation4BeforeUpdate = await UserGroupRelation.findOne({
  278. relatedGroup: userGroup4,
  279. relatedUser: userId1,
  280. });
  281. expect(userGroupRelation4BeforeUpdate).not.toBeNull();
  282. // userGroup5 has not userId1
  283. const userGroupRelation5BeforeUpdate = await UserGroupRelation.findOne({
  284. relatedGroup: userGroup5,
  285. relatedUser: userId1,
  286. });
  287. expect(userGroupRelation5BeforeUpdate).toBeNull();
  288. // update userGroup4's parent with userGroup5 (forceUpdate: true)
  289. const forceUpdateParents = true;
  290. const updatedUserGroup = await userGroupService.updateGroup(
  291. userGroup4?._id,
  292. userGroup4?.name,
  293. userGroup4?.description,
  294. groupId5,
  295. forceUpdateParents,
  296. );
  297. expect(updatedUserGroup.parent).toStrictEqual(groupId5);
  298. // userGroup5 should have userId1
  299. const userGroupRelation5AfterUpdate = await UserGroupRelation.findOne({
  300. relatedGroup: groupId5,
  301. relatedUser: userGroupRelation4BeforeUpdate?.relatedUser,
  302. });
  303. expect(userGroupRelation5AfterUpdate).not.toBeNull();
  304. });
  305. test('User should be included to parent group (3 groups ver)', async () => {
  306. const userGroup8 = await UserGroup.findOne({
  307. _id: groupId8,
  308. parent: null,
  309. });
  310. // userGroup7 has not userId1
  311. const userGroupRelation6BeforeUpdate = await UserGroupRelation.findOne({
  312. relatedGroup: groupId6,
  313. relatedUser: userId1,
  314. });
  315. const userGroupRelation7BeforeUpdate = await UserGroupRelation.findOne({
  316. relatedGroup: groupId7,
  317. relatedUser: userId1,
  318. });
  319. const userGroupRelation8BeforeUpdate = await UserGroupRelation.findOne({
  320. relatedGroup: groupId8,
  321. relatedUser: userId1,
  322. });
  323. expect(userGroupRelation6BeforeUpdate).not.toBeNull();
  324. // userGroup7 does not have userId1
  325. expect(userGroupRelation7BeforeUpdate).toBeNull();
  326. expect(userGroupRelation8BeforeUpdate).not.toBeNull();
  327. // update userGroup8's parent with userGroup7 (forceUpdate: true)
  328. const forceUpdateParents = true;
  329. await userGroupService.updateGroup(
  330. userGroup8?._id,
  331. userGroup8?.name,
  332. userGroup8?.description,
  333. groupId7,
  334. forceUpdateParents,
  335. );
  336. const userGroupRelation6AfterUpdate = await UserGroupRelation.findOne({
  337. relatedGroup: groupId6,
  338. relatedUser: userId1,
  339. });
  340. const userGroupRelation7AfterUpdate = await UserGroupRelation.findOne({
  341. relatedGroup: groupId7,
  342. relatedUser: userId1,
  343. });
  344. const userGroupRelation8AfterUpdate = await UserGroupRelation.findOne({
  345. relatedGroup: groupId8,
  346. relatedUser: userId1,
  347. });
  348. expect(userGroupRelation6AfterUpdate).not.toBeNull();
  349. // userGroup7 should have userId1
  350. expect(userGroupRelation7AfterUpdate).not.toBeNull();
  351. expect(userGroupRelation8AfterUpdate).not.toBeNull();
  352. });
  353. test('Should throw an error when trying to choose parent from descendant groups.', async () => {
  354. const userGroup9 = await UserGroup.findOne({
  355. _id: groupId9,
  356. parent: null,
  357. });
  358. const userGroup10 = await UserGroup.findOne({
  359. _id: groupId10,
  360. parent: groupId9,
  361. });
  362. const userGroupRelation9BeforeUpdate = await UserGroupRelation.findOne({
  363. relatedGroup: userGroup9?._id,
  364. relatedUser: userId1,
  365. });
  366. const userGroupRelation10BeforeUpdate = await UserGroupRelation.findOne({
  367. relatedGroup: userGroup10?._id,
  368. relatedUser: userId1,
  369. });
  370. expect(userGroupRelation9BeforeUpdate).not.toBeNull();
  371. expect(userGroupRelation10BeforeUpdate).not.toBeNull();
  372. const result = userGroupService.updateGroup(
  373. userGroup9?._id,
  374. userGroup9?.name,
  375. userGroup9?.description,
  376. userGroup10?._id,
  377. );
  378. await expect(result).rejects.toThrow(
  379. 'It is not allowed to choose parent from descendant groups.',
  380. );
  381. });
  382. });
  383. describe('removeUserByUsername', () => {
  384. test('User should be deleted from child groups when the user excluded from the parent group', async () => {
  385. const userGroup11 = await UserGroup.findOne({
  386. _id: groupId11,
  387. parent: null,
  388. });
  389. const userGroup12 = await UserGroup.findOne({
  390. _id: groupId12,
  391. parent: groupId11,
  392. });
  393. // Both groups have user1
  394. const userGroupRelation11BeforeRemove = await UserGroupRelation.findOne({
  395. relatedGroup: userGroup11?._id,
  396. relatedUser: userId1,
  397. });
  398. const userGroupRelation12BeforeRemove = await UserGroupRelation.findOne({
  399. relatedGroup: userGroup12?._id,
  400. relatedUser: userId1,
  401. });
  402. expect(userGroupRelation11BeforeRemove).not.toBeNull();
  403. expect(userGroupRelation12BeforeRemove).not.toBeNull();
  404. // remove user1 from the parent group
  405. await userGroupService.removeUserByUsername(
  406. userGroup11?._id,
  407. 'ug_test_user1',
  408. );
  409. // Both groups have not user1
  410. const userGroupRelation11AfterRemove = await UserGroupRelation.findOne({
  411. relatedGroup: userGroup11?._id,
  412. relatedUser: userId1,
  413. });
  414. const userGroupRelation12AfterRemove = await UserGroupRelation.findOne({
  415. relatedGroup: userGroup12?._id,
  416. relatedUser: userId1,
  417. });
  418. await expect(userGroupRelation11AfterRemove).toBeNull();
  419. await expect(userGroupRelation12AfterRemove).toBeNull();
  420. });
  421. });
  422. describe('removeCompletelyByRootGroupId', () => {
  423. describe('when action is public', () => {
  424. test('Should remove the group and its descendants and publicize pages that are only visible to the groups to be removed', async () => {
  425. const userGroup13 = await UserGroup.findOne({ _id: groupId13 });
  426. const userGroup14 = await UserGroup.findOne({ _id: groupId14 });
  427. expect(userGroup13).not.toBeNull();
  428. expect(userGroup14).not.toBeNull();
  429. const canBePublicized = await Page.findOne({ _id: pageId1 });
  430. const cannotBePublicized = await Page.findOne({ _id: pageId2 });
  431. expect(canBePublicized?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  432. expect(normalizeGrantedGroups(canBePublicized?.grantedGroups)).toEqual(
  433. expect.arrayContaining([
  434. { item: groupId13, type: GroupType.userGroup },
  435. { item: groupId14, type: GroupType.userGroup },
  436. ]),
  437. );
  438. expect(
  439. normalizeGrantedGroups(canBePublicized?.grantedGroups)?.length,
  440. ).toBe(2);
  441. expect(cannotBePublicized?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  442. expect(
  443. normalizeGrantedGroups(cannotBePublicized?.grantedGroups),
  444. ).toEqual(
  445. expect.arrayContaining([
  446. { item: groupId13, type: GroupType.userGroup },
  447. { item: groupId15, type: GroupType.userGroup },
  448. ]),
  449. );
  450. expect(
  451. normalizeGrantedGroups(cannotBePublicized?.grantedGroups)?.length,
  452. ).toBe(2);
  453. await userGroupService.removeCompletelyByRootGroupId(
  454. groupId13,
  455. PageActionOnGroupDelete.publicize,
  456. user1,
  457. );
  458. const userGroup13AfterDeleteProcess = await UserGroup.findOne({
  459. _id: groupId13,
  460. });
  461. const userGroup14AfterDeleteProcess = await UserGroup.findOne({
  462. _id: groupId14,
  463. });
  464. expect(userGroup13AfterDeleteProcess).toBeNull();
  465. expect(userGroup14AfterDeleteProcess).toBeNull();
  466. const canBePublicizedAfterDeleteProcess = await Page.findOne({
  467. _id: pageId1,
  468. });
  469. const cannotBePublicizedAfterDeleteProcess = await Page.findOne({
  470. _id: pageId2,
  471. });
  472. expect(canBePublicizedAfterDeleteProcess?.grant).toBe(
  473. PageGrant.GRANT_PUBLIC,
  474. );
  475. expect(
  476. normalizeGrantedGroups(
  477. canBePublicizedAfterDeleteProcess?.grantedGroups,
  478. ),
  479. ).toEqual([]);
  480. expect(cannotBePublicizedAfterDeleteProcess?.grant).toBe(
  481. PageGrant.GRANT_USER_GROUP,
  482. );
  483. expect(
  484. normalizeGrantedGroups(
  485. cannotBePublicizedAfterDeleteProcess?.grantedGroups,
  486. ),
  487. ).toEqual(
  488. expect.arrayContaining([
  489. { item: groupId15, type: GroupType.userGroup },
  490. ]),
  491. );
  492. expect(
  493. normalizeGrantedGroups(
  494. cannotBePublicizedAfterDeleteProcess?.grantedGroups,
  495. )?.length,
  496. ).toBe(1);
  497. });
  498. });
  499. });
  500. });