keycloak-user-group-sync.integ.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import { configManager } from '~/server/service/config-manager';
  2. import { KeycloakUserGroupSyncService } from './keycloak-user-group-sync';
  3. vi.mock('@keycloak/keycloak-admin-client', () => {
  4. return {
  5. default: class {
  6. auth() {}
  7. groups = {
  8. // mock group search on Keycloak
  9. find: () => {
  10. return [
  11. // root node
  12. {
  13. id: 'groupId1',
  14. name: 'grandParentGroup',
  15. subGroups: [
  16. {
  17. id: 'groupId2',
  18. name: 'parentGroup',
  19. subGroups: [
  20. {
  21. id: 'groupId3',
  22. name: 'childGroup',
  23. },
  24. ],
  25. },
  26. ],
  27. },
  28. // another root node
  29. {
  30. id: 'groupId4',
  31. name: 'rootGroup',
  32. },
  33. ];
  34. },
  35. // mock group detail
  36. findOne: (payload) => {
  37. if (payload?.id === 'groupId1') {
  38. return Promise.resolve(
  39. {
  40. id: 'groupId1',
  41. name: 'grandParentGroup',
  42. attributes: {
  43. description: ['this is a grand parent group'],
  44. },
  45. },
  46. );
  47. }
  48. if (payload?.id === 'groupId2') {
  49. return Promise.resolve(
  50. {
  51. id: 'groupId2',
  52. name: 'parentGroup',
  53. attributes: {
  54. description: ['this is a parent group'],
  55. },
  56. },
  57. );
  58. }
  59. if (payload?.id === 'groupId3') {
  60. return Promise.resolve(
  61. {
  62. id: 'groupId3',
  63. name: 'childGroup',
  64. attributes: {
  65. description: ['this is a child group'],
  66. },
  67. },
  68. );
  69. }
  70. if (payload?.id === 'groupId4') {
  71. return Promise.resolve(
  72. {
  73. id: 'groupId3',
  74. name: 'childGroup',
  75. attributes: {
  76. description: ['this is a root group'],
  77. },
  78. },
  79. );
  80. }
  81. return Promise.reject(new Error('not found'));
  82. },
  83. // mock group users
  84. listMembers: (payload) => {
  85. // set 'first' condition to 0 (the first member request to server) or else it will result in infinite loop
  86. if (payload?.id === 'groupId1' && payload?.first === 0) {
  87. return Promise.resolve([
  88. {
  89. id: 'userId1',
  90. username: 'grandParentGroupUser',
  91. email: 'user@grandParentGroup.com',
  92. },
  93. ]);
  94. }
  95. if (payload?.id === 'groupId2' && payload?.first === 0) {
  96. return Promise.resolve([
  97. {
  98. id: 'userId2',
  99. username: 'parentGroupUser',
  100. email: 'user@parentGroup.com',
  101. },
  102. ]);
  103. }
  104. if (payload?.id === 'groupId3' && payload?.first === 0) {
  105. return Promise.resolve([
  106. {
  107. id: 'userId3',
  108. username: 'childGroupUser',
  109. email: 'user@childGroup.com',
  110. },
  111. ]);
  112. }
  113. if (payload?.id === 'groupId4' && payload?.first === 0) {
  114. return Promise.resolve([
  115. {
  116. id: 'userId4',
  117. username: 'rootGroupUser',
  118. email: 'user@rootGroup.com',
  119. },
  120. ]);
  121. }
  122. return Promise.resolve([]);
  123. },
  124. };
  125. },
  126. };
  127. });
  128. describe('KeycloakUserGroupSyncService.generateExternalUserGroupTrees', () => {
  129. let keycloakUserGroupSyncService: KeycloakUserGroupSyncService;
  130. const configParams = {
  131. 'external-user-group:keycloak:host': 'http://dummy-keycloak-host.com',
  132. 'external-user-group:keycloak:groupRealm': 'myrealm',
  133. 'external-user-group:keycloak:groupSyncClientRealm': 'myrealm',
  134. 'external-user-group:keycloak:groupDescriptionAttribute': 'description',
  135. 'external-user-group:keycloak:groupSyncClientID': 'admin-cli',
  136. 'external-user-group:keycloak:groupSyncClientSecret': '123456',
  137. };
  138. beforeAll(async() => {
  139. await configManager.updateConfigsInTheSameNamespace('crowi', configParams, true);
  140. keycloakUserGroupSyncService = new KeycloakUserGroupSyncService(null, null);
  141. });
  142. it('creates ExternalUserGroupTrees', async() => {
  143. const rootNodes = await keycloakUserGroupSyncService?.generateExternalUserGroupTrees();
  144. expect(rootNodes?.length).toBe(2);
  145. // check grandParentGroup
  146. const grandParentNode = rootNodes?.find(node => node.id === 'groupId1');
  147. const expectedChildNode = {
  148. id: 'groupId3',
  149. userInfos: [{
  150. id: 'userId3',
  151. username: 'childGroupUser',
  152. email: 'user@childGroup.com',
  153. }],
  154. childGroupNodes: [],
  155. name: 'childGroup',
  156. description: 'this is a child group',
  157. };
  158. const expectedParentNode = {
  159. id: 'groupId2',
  160. userInfos: [{
  161. id: 'userId2',
  162. username: 'parentGroupUser',
  163. email: 'user@parentGroup.com',
  164. }],
  165. childGroupNodes: [expectedChildNode],
  166. name: 'parentGroup',
  167. description: 'this is a parent group',
  168. };
  169. const expectedGrandParentNode = {
  170. id: 'groupId1',
  171. userInfos: [{
  172. id: 'userId1',
  173. username: 'grandParentGroupUser',
  174. email: 'user@grandParentGroup.com',
  175. }],
  176. childGroupNodes: [expectedParentNode],
  177. name: 'grandParentGroup',
  178. description: 'this is a grand parent group',
  179. };
  180. expect(grandParentNode).toStrictEqual(expectedGrandParentNode);
  181. // check rootGroup
  182. const rootNode = rootNodes?.find(node => node.id === 'groupId4');
  183. const expectedRootNode = {
  184. id: 'groupId4',
  185. userInfos: [{
  186. id: 'userId4',
  187. username: 'rootGroupUser',
  188. email: 'user@rootGroup.com',
  189. }],
  190. childGroupNodes: [],
  191. name: 'rootGroup',
  192. description: 'this is a root group',
  193. };
  194. expect(rootNode).toStrictEqual(expectedRootNode);
  195. });
  196. });