create_groups_nextcloud_scope.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import dotenv from 'dotenv';
  2. import axios from 'axios';
  3. // Load environment variables
  4. dotenv.config();
  5. console.log('Environment variables loaded.');
  6. // Configuration constants
  7. const KEYCLOAK_URL = process.env.KEYCLOAK_URL || 'https://auth.mrx8086.com';
  8. const ADMIN_USERNAME = process.env.KEYCLOAK_ADMIN_USER;
  9. const ADMIN_PASSWORD = process.env.KEYCLOAK_ADMIN_PASSWORD;
  10. const REALM_NAME = 'office-automation';
  11. console.log('Configuration constants set:', { KEYCLOAK_URL, ADMIN_USERNAME, REALM_NAME });
  12. // Helper function for API error handling
  13. const handleAxiosError = (error, operation, config, response) => {
  14. console.error(`Error during ${operation}:`);
  15. if (config) {
  16. console.error('Request:', {
  17. method: config.method,
  18. url: config.url,
  19. headers: config.headers,
  20. data: config.data,
  21. });
  22. }
  23. if (error.response) {
  24. console.error('Response:', {
  25. status: error.response.status,
  26. data: error.response.data
  27. });
  28. } else {
  29. console.error('Error Message:', error.message);
  30. }
  31. throw error;
  32. };
  33. // Get Admin Token
  34. async function getAdminToken() {
  35. console.log('Getting admin token...');
  36. try {
  37. const response = await axios.post(
  38. `${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token`,
  39. new URLSearchParams({
  40. 'client_id': 'admin-cli',
  41. 'username': ADMIN_USERNAME,
  42. 'password': ADMIN_PASSWORD,
  43. 'grant_type': 'password'
  44. }),
  45. {
  46. headers: {
  47. 'Content-Type': 'application/x-www-form-urlencoded'
  48. }
  49. }
  50. );
  51. console.log('Admin token received.');
  52. return response.data.access_token;
  53. } catch (error) {
  54. handleAxiosError(error, 'getting admin token');
  55. }
  56. }
  57. async function getClientScope(token, scopeName) {
  58. console.log(`Getting client scope "${scopeName}"...`);
  59. try {
  60. const response = await axios.get(
  61. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes`,
  62. {
  63. headers: {
  64. 'Authorization': `Bearer ${token}`
  65. },
  66. params: {
  67. name: scopeName
  68. }
  69. }
  70. );
  71. if (response.data.length === 0) {
  72. console.log(`Client Scope "${scopeName}" not found.`);
  73. return null;
  74. }
  75. console.log(`Client scope "${scopeName}" found:`, response.data);
  76. return response.data[0];
  77. } catch (error) {
  78. handleAxiosError(error, `getting client scope "${scopeName}"`, error.config, error.response);
  79. return null;
  80. }
  81. }
  82. async function createClientScope(token, scopeName, consentScreenText) {
  83. console.log(`Attempting to create client scope "${scopeName}"...`);
  84. try {
  85. const response = await axios.post(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes`,
  86. {
  87. "name": scopeName,
  88. "protocol": "openid-connect",
  89. "attributes": {},
  90. "consentScreenText": consentScreenText,
  91. "includeInTokenScope": true
  92. },
  93. {
  94. headers: {
  95. 'Authorization': `Bearer ${token}`,
  96. 'Content-Type': 'application/json'
  97. }
  98. });
  99. console.log(`Client scope "${scopeName}" created successfully`);
  100. return response.data;
  101. } catch (error) {
  102. console.error(`Error creating client scope "${scopeName}":`, error);
  103. handleAxiosError(error, `creating ${scopeName} client scope`, error.config, error.response);
  104. return null;
  105. }
  106. }
  107. async function createMapper(token, clientScopeId, mapperName) {
  108. console.log(`Attempting to create mapper "${mapperName}" for client scope ID "${clientScopeId}"...`);
  109. try {
  110. const response = await axios.post(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes/${clientScopeId}/protocol-mappers/models`,
  111. {
  112. "name": mapperName,
  113. "protocol": "openid-connect",
  114. "protocolMapper": "oidc-group-membership-mapper",
  115. "config": {
  116. "full.path": "false",
  117. "id.token.claim": "false",
  118. "access.token.claim": "true",
  119. "userinfo.token.claim": "true",
  120. "claim.name": "groups",
  121. "add.to.introspection": "false"
  122. }
  123. },
  124. {
  125. headers: {
  126. 'Authorization': `Bearer ${token}`,
  127. 'Content-Type': 'application/json'
  128. }
  129. });
  130. console.log(`Mapper "${mapperName}" created for client scope ID "${clientScopeId}"`);
  131. return response.data;
  132. } catch (error) {
  133. console.error(`Error creating mapper "${mapperName}" for client scope ID "${clientScopeId}":`, error);
  134. handleAxiosError(error, `creating mapper "${mapperName}"`, error.config, error.response);
  135. return null;
  136. }
  137. }
  138. async function createGroupsNextcloudScope(token) {
  139. const scopeName = "groups-nextcloud-test";
  140. const mapperName = "groups-mapper-test";
  141. console.log("Starting createGroupsNextcloudScope");
  142. let clientScope = await getClientScope(token, scopeName);
  143. if (!clientScope) {
  144. clientScope = await createClientScope(token, scopeName, "Grant access to user groups in nextcloud");
  145. } else {
  146. console.log(`Client scope "${scopeName}" exists, skipping creation. Details:`, clientScope);
  147. }
  148. if (clientScope) {
  149. console.log("Check for mappers in groups-nextcloud-test scope");
  150. try {
  151. const mappersResponse = await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes/${clientScope.id}/protocol-mappers/models`,
  152. {
  153. headers: {
  154. 'Authorization': `Bearer ${token}`
  155. }
  156. });
  157. if (!mappersResponse.data.find(m => m.name === mapperName)) {
  158. await createMapper(token, clientScope.id, mapperName);
  159. } else {
  160. console.log(`Mapper "${mapperName}" exists, skipping creation. Details:`, mappersResponse.data.find(m => m.name === mapperName));
  161. }
  162. } catch (error) {
  163. console.error("Error checking for mappers:", error);
  164. handleAxiosError(error, `checking mappers for ${scopeName}`, error.config, error.response);
  165. }
  166. }
  167. console.log("Finished createGroupsNextcloudScope");
  168. }
  169. async function createSimpleTestScope(token) {
  170. const uniqueSuffix = Date.now();
  171. const simpleScopeName = `test-simple-scope-${uniqueSuffix}`;
  172. console.log(`Attempting to create simple test scope with name: ${simpleScopeName}`);
  173. const simpleScope = await createClientScope(token, simpleScopeName, "Simple test scope");
  174. if (simpleScope) {
  175. console.log(`Successfully created simple test scope:`, simpleScope);
  176. }
  177. }
  178. // Main function
  179. async function setupRealm() {
  180. try {
  181. console.log('Starting Keycloak setup...');
  182. const token = await getAdminToken();
  183. // Create a simple test client scope
  184. await createSimpleTestScope(token);
  185. // Create client scope groups-nextcloud
  186. await createGroupsNextcloudScope(token);
  187. console.log('Setup completed successfully');
  188. } catch (error) {
  189. console.error('Setup failed:', error);
  190. process.exit(1);
  191. }
  192. }
  193. // Execute the script
  194. setupRealm();