user-groups.test.ts 15 KB

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