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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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. // name is undefined
  154. userInfos: [{
  155. id: 'parentGroupUser',
  156. username: 'parentGroupUser',
  157. name: 'Parent Group User',
  158. email: 'user@parentGroup.com',
  159. }],
  160. childGroupNodes: [expectedChildNode],
  161. name: 'parentGroup',
  162. description: 'this is a parent group',
  163. };
  164. const expectedGrandParentNode = {
  165. id: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
  166. // email is undefined
  167. userInfos: [{
  168. id: 'grandParentGroupUser',
  169. username: 'grandParentGroupUser',
  170. name: 'Grand Parent Group User',
  171. email: 'user@grandParentGroup.com',
  172. }],
  173. childGroupNodes: [expectedParentNode],
  174. name: 'grandParentGroup',
  175. description: 'this is a grand parent group',
  176. };
  177. expect(grandParentNode).toStrictEqual(expectedGrandParentNode);
  178. // check rootGroup
  179. const rootNode = rootNodes.find(node => node.id === 'cn=rootGroup,ou=groups,dc=example,dc=org');
  180. const expectedRootNode = {
  181. id: 'cn=rootGroup,ou=groups,dc=example,dc=org',
  182. // email is undefined
  183. userInfos: [{
  184. id: 'rootGroupUser',
  185. username: 'rootGroupUser',
  186. name: 'Root Group User',
  187. email: 'user@rootGroup.com',
  188. }],
  189. childGroupNodes: [],
  190. name: 'rootGroup',
  191. description: 'this is a root group',
  192. };
  193. expect(rootNode).toStrictEqual(expectedRootNode);
  194. });
  195. });
  196. describe('When there is a circular reference in group tree', () => {
  197. it('rejects creating ExternalUserGroupTrees', async() => {
  198. // mock search on LDAP server
  199. mockLdapSearch.mockImplementation((filter, base) => {
  200. if (base === 'ou=groups,dc=example,dc=org') {
  201. // search groups
  202. return Promise.resolve([
  203. // childGroup and parentGroup have circular reference
  204. {
  205. objectName: 'cn=childGroup,ou=groups,dc=example,dc=org',
  206. attributes: [
  207. { type: 'cn', values: ['childGroup'] },
  208. { type: 'description', values: ['this is a child group'] },
  209. {
  210. type: 'member',
  211. values: ['cn=parentGroup,ou=groups,dc=example,dc=org'],
  212. },
  213. ],
  214. },
  215. {
  216. objectName: 'cn=parentGroup,ou=groups,dc=example,dc=org',
  217. attributes: [
  218. { type: 'cn', values: ['parentGroup'] },
  219. { type: 'description', values: ['this is a parent group'] },
  220. {
  221. type: 'member',
  222. values: ['cn=childGroup,ou=groups,dc=example,dc=org'],
  223. },
  224. ],
  225. },
  226. {
  227. objectName: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
  228. attributes: [
  229. { type: 'cn', values: ['grandParentGroup'] },
  230. { type: 'description', values: ['this is a grand parent group'] },
  231. {
  232. type: 'member',
  233. values: ['cn=parentGroup,ou=groups,dc=example,dc=org'],
  234. },
  235. ],
  236. },
  237. ]);
  238. }
  239. return Promise.reject(new Error('not found'));
  240. });
  241. await expect(ldapGroupSyncService.generateExternalUserGroupTrees()).rejects.toThrow('external_user_group.ldap.circular_reference');
  242. });
  243. });
  244. });