ldap-user-group-sync.test.ts 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. import LdapUserGroupSyncService from '../../../src/features/external-user-group/server/service/ldap-user-group-sync';
  2. import { configManager } from '../../../src/server/service/config-manager';
  3. import LdapService from '../../../src/server/service/ldap';
  4. import PassportService from '../../../src/server/service/passport';
  5. import { getInstance } from '../setup-crowi';
  6. describe('LdapUserGroupSyncService.generateExternalUserGroupTrees', () => {
  7. let crowi;
  8. let ldapGroupSyncService: LdapUserGroupSyncService;
  9. const configParams = {
  10. 'security:passport-ldap:attrMapName': 'name',
  11. 'external-user-group:ldap:groupChildGroupAttribute': 'member',
  12. 'external-user-group:ldap:groupMembershipAttribute': 'member',
  13. 'external-user-group:ldap:groupNameAttribute': 'cn',
  14. 'external-user-group:ldap:groupDescriptionAttribute': 'description',
  15. 'external-user-group:ldap:groupMembershipAttributeType': 'DN',
  16. 'external-user-group:ldap:groupSearchBase': 'ou=groups,dc=example,dc=org',
  17. };
  18. jest.mock('../../../src/server/service/ldap');
  19. const mockLdapSearch = jest.spyOn(LdapService.prototype, 'search');
  20. beforeAll(async() => {
  21. crowi = await getInstance();
  22. const passportService = new PassportService(crowi);
  23. ldapGroupSyncService = new LdapUserGroupSyncService(passportService);
  24. await configManager.updateConfigsInTheSameNamespace('crowi', configParams, true);
  25. });
  26. describe('When there is no circular reference in group tree', () => {
  27. it('creates ExternalUserGroupTrees', async() => {
  28. // mock search on LDAP server
  29. mockLdapSearch.mockImplementation((filter, base) => {
  30. if (base === 'ou=groups,dc=example,dc=org') {
  31. // search groups
  32. return Promise.resolve([
  33. {
  34. objectName: 'cn=childGroup,ou=groups,dc=example,dc=org',
  35. attributes: [
  36. { type: 'cn', values: ['childGroup'] },
  37. { type: 'description', values: ['this is a child group'] },
  38. {
  39. type: 'member',
  40. values: ['cn=childGroupUser,ou=users,dc=example,dc=org'],
  41. },
  42. ],
  43. },
  44. {
  45. objectName: 'cn=parentGroup,ou=groups,dc=example,dc=org',
  46. attributes: [
  47. { type: 'cn', values: ['parentGroup'] },
  48. { type: 'description', values: ['this is a parent group'] },
  49. {
  50. type: 'member',
  51. values: ['cn=childGroup,ou=groups,dc=example,dc=org', 'cn=parentGroupUser,ou=users,dc=example,dc=org'],
  52. },
  53. ],
  54. },
  55. // root node
  56. {
  57. objectName: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
  58. attributes: [
  59. { type: 'cn', values: ['grandParentGroup'] },
  60. { type: 'description', values: ['this is a grand parent group'] },
  61. {
  62. type: 'member',
  63. values: ['cn=parentGroup,ou=groups,dc=example,dc=org', 'cn=grandParentGroupUser,ou=users,dc=example,dc=org'],
  64. },
  65. ],
  66. },
  67. // another root node
  68. {
  69. objectName: 'cn=rootGroup,ou=groups,dc=example,dc=org',
  70. attributes: [
  71. { type: 'cn', values: ['rootGroup'] },
  72. { type: 'description', values: ['this is a root group'] },
  73. {
  74. type: 'member',
  75. values: ['cn=rootGroupUser,ou=users,dc=example,dc=org'],
  76. },
  77. ],
  78. },
  79. ]);
  80. }
  81. if (base === 'cn=childGroupUser,ou=users,dc=example,dc=org') {
  82. // search childGroupUser
  83. return Promise.resolve([
  84. {
  85. objectName: 'cn=childGroupUser,ou=users,dc=example,dc=org',
  86. attributes: [
  87. { type: 'name', values: ['Child Group User'] },
  88. { type: 'uid', values: ['childGroupUser'] },
  89. { type: 'mail', values: ['user@childGroup.com'] },
  90. ],
  91. },
  92. ]);
  93. }
  94. // search parentGroupUser
  95. if (base === 'cn=parentGroupUser,ou=users,dc=example,dc=org') {
  96. return Promise.resolve([
  97. {
  98. objectName: 'cn=parentGroupUser,ou=users,dc=example,dc=org',
  99. attributes: [
  100. { type: 'name', values: ['Parent Group User'] },
  101. { type: 'uid', values: ['parentGroupUser'] },
  102. { type: 'mail', values: ['user@parentGroup.com'] },
  103. ],
  104. },
  105. ]);
  106. }
  107. // search grandParentGroupUser
  108. if (base === 'cn=grandParentGroupUser,ou=users,dc=example,dc=org') {
  109. return Promise.resolve([
  110. {
  111. objectName: 'cn=grandParentGroupUser,ou=users,dc=example,dc=org',
  112. attributes: [
  113. { type: 'name', values: ['Grand Parent Group User'] },
  114. { type: 'uid', values: ['grandParentGroupUser'] },
  115. { type: 'mail', values: ['user@grandParentGroup.com'] },
  116. ],
  117. },
  118. ]);
  119. }
  120. // search rootGroupUser
  121. if (base === 'cn=rootGroupUser,ou=users,dc=example,dc=org') {
  122. return Promise.resolve([
  123. {
  124. objectName: 'cn=rootGroupUser,ou=users,dc=example,dc=org',
  125. attributes: [
  126. { type: 'name', values: ['Root Group User'] },
  127. { type: 'uid', values: ['rootGroupUser'] },
  128. { type: 'mail', values: ['user@rootGroup.com'] },
  129. ],
  130. },
  131. ]);
  132. }
  133. return Promise.reject(new Error('not found'));
  134. });
  135. const rootNodes = await ldapGroupSyncService.generateExternalUserGroupTrees();
  136. expect(rootNodes.length).toBe(2);
  137. // check grandParentGroup
  138. const grandParentNode = rootNodes.find(node => node.id === 'cn=grandParentGroup,ou=groups,dc=example,dc=org');
  139. const expectedChildNode = {
  140. id: 'cn=childGroup,ou=groups,dc=example,dc=org',
  141. userInfos: [{
  142. id: 'childGroupUser',
  143. username: 'childGroupUser',
  144. name: 'Child Group User',
  145. email: 'user@childGroup.com',
  146. }],
  147. childGroupNodes: [],
  148. name: 'childGroup',
  149. description: 'this is a child group',
  150. };
  151. const expectedParentNode = {
  152. id: 'cn=parentGroup,ou=groups,dc=example,dc=org',
  153. userInfos: [{
  154. id: 'parentGroupUser',
  155. username: 'parentGroupUser',
  156. name: 'Parent Group User',
  157. email: 'user@parentGroup.com',
  158. }],
  159. childGroupNodes: [expectedChildNode],
  160. name: 'parentGroup',
  161. description: 'this is a parent group',
  162. };
  163. const expectedGrandParentNode = {
  164. id: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
  165. userInfos: [{
  166. id: 'grandParentGroupUser',
  167. username: 'grandParentGroupUser',
  168. name: 'Grand Parent Group User',
  169. email: 'user@grandParentGroup.com',
  170. }],
  171. childGroupNodes: [expectedParentNode],
  172. name: 'grandParentGroup',
  173. description: 'this is a grand parent group',
  174. };
  175. expect(grandParentNode).toStrictEqual(expectedGrandParentNode);
  176. // check rootGroup
  177. const rootNode = rootNodes.find(node => node.id === 'cn=rootGroup,ou=groups,dc=example,dc=org');
  178. const expectedRootNode = {
  179. id: 'cn=rootGroup,ou=groups,dc=example,dc=org',
  180. userInfos: [{
  181. id: 'rootGroupUser',
  182. username: 'rootGroupUser',
  183. name: 'Root Group User',
  184. email: 'user@rootGroup.com',
  185. }],
  186. childGroupNodes: [],
  187. name: 'rootGroup',
  188. description: 'this is a root group',
  189. };
  190. expect(rootNode).toStrictEqual(expectedRootNode);
  191. });
  192. });
  193. describe('When there is a circular reference in group tree', () => {
  194. it('rejects creating ExternalUserGroupTrees', async() => {
  195. // mock search on LDAP server
  196. mockLdapSearch.mockImplementation((filter, base) => {
  197. if (base === 'ou=groups,dc=example,dc=org') {
  198. // search groups
  199. return Promise.resolve([
  200. // childGroup and parentGroup have circular reference
  201. {
  202. objectName: 'cn=childGroup,ou=groups,dc=example,dc=org',
  203. attributes: [
  204. { type: 'cn', values: ['childGroup'] },
  205. { type: 'description', values: ['this is a child group'] },
  206. {
  207. type: 'member',
  208. values: ['cn=parentGroup,ou=groups,dc=example,dc=org'],
  209. },
  210. ],
  211. },
  212. {
  213. objectName: 'cn=parentGroup,ou=groups,dc=example,dc=org',
  214. attributes: [
  215. { type: 'cn', values: ['parentGroup'] },
  216. { type: 'description', values: ['this is a parent group'] },
  217. {
  218. type: 'member',
  219. values: ['cn=childGroup,ou=groups,dc=example,dc=org'],
  220. },
  221. ],
  222. },
  223. {
  224. objectName: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
  225. attributes: [
  226. { type: 'cn', values: ['grandParentGroup'] },
  227. { type: 'description', values: ['this is a grand parent group'] },
  228. {
  229. type: 'member',
  230. values: ['cn=parentGroup,ou=groups,dc=example,dc=org'],
  231. },
  232. ],
  233. },
  234. ]);
  235. }
  236. return Promise.reject(new Error('not found'));
  237. });
  238. await expect(ldapGroupSyncService.generateExternalUserGroupTrees()).rejects.toThrow('external_user_group.ldap.circular_reference');
  239. });
  240. });
  241. });