setup_realm.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  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. // Client IDs and their configuration
  13. const CLIENTS = {
  14. [process.env.NEXTCLOUD_CLIENT_ID || 'nextcloud']: {
  15. redirectUris: [
  16. `https://cloud.mrx8086.com/apps/sociallogin/custom_oidc/keycloak`,
  17. `https://cloud.mrx8086.com/apps/user_oidc/code`
  18. ],
  19. postLogoutRedirectUris: ["https://cloud.mrx8086.com/*"]
  20. },
  21. [process.env.PAPERLESS_CLIENT_ID || 'paperless']: {
  22. redirectUris: ["https://docs.mrx8086.com/*"]
  23. },
  24. [process.env.NODERED_CLIENT_ID || 'nodered']: {
  25. redirectUris: ["https://automate.mrx8086.com/*"]
  26. }
  27. };
  28. console.log('CLIENTS configuration:', CLIENTS);
  29. // Helper function for API error handling
  30. const handleAxiosError = (error, operation, config, response) => {
  31. console.error(`Error during ${operation}:`);
  32. if (config) {
  33. console.error('Request:', {
  34. method: config.method,
  35. url: config.url,
  36. headers: config.headers,
  37. data: config.data,
  38. });
  39. }
  40. if (error.response) {
  41. console.error('Response:', {
  42. status: error.response.status,
  43. data: error.response.data
  44. });
  45. } else {
  46. console.error('Error Message:', error.message);
  47. }
  48. throw error;
  49. };
  50. // Get Admin Token
  51. async function getAdminToken() {
  52. console.log('Getting admin token...');
  53. try {
  54. const response = await axios.post(
  55. `${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token`,
  56. new URLSearchParams({
  57. 'client_id': 'admin-cli',
  58. 'username': ADMIN_USERNAME,
  59. 'password': ADMIN_PASSWORD,
  60. 'grant_type': 'password'
  61. }),
  62. {
  63. headers: {
  64. 'Content-Type': 'application/x-www-form-urlencoded'
  65. }
  66. }
  67. );
  68. console.log('Admin token received.');
  69. return response.data.access_token;
  70. } catch (error) {
  71. handleAxiosError(error, 'getting admin token');
  72. }
  73. }
  74. // Check if Realm exists
  75. async function checkRealmExists(token) {
  76. console.log(`Checking if realm ${REALM_NAME} exists...`);
  77. try {
  78. await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}`, {
  79. headers: { 'Authorization': `Bearer ${token}` }
  80. });
  81. console.log(`Realm ${REALM_NAME} exists.`);
  82. return true;
  83. } catch (error) {
  84. if (error.response?.status === 404) {
  85. console.log(`Realm ${REALM_NAME} does not exist.`);
  86. return false;
  87. }
  88. handleAxiosError(error, 'checking realm existence');
  89. }
  90. }
  91. // Function to get client information by clientId
  92. async function getClientByClientId(token, clientId) {
  93. console.log(`Getting client information for ${clientId}...`);
  94. try {
  95. const response = await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients`, {
  96. headers: { 'Authorization': `Bearer ${token}` },
  97. params: { clientId }
  98. });
  99. if (response.data.length === 0) {
  100. console.log(`Client ${clientId} not found`);
  101. return null;
  102. }
  103. console.log(`Client ${clientId} found.`);
  104. return response.data[0];
  105. } catch (error) {
  106. handleAxiosError(error, `getting client ${clientId}`);
  107. return null;
  108. }
  109. }
  110. // Check if client exists
  111. const checkClientExists = async (token, clientId) => !!await getClientByClientId(token, clientId);
  112. // Get client mappers by client ID
  113. async function getClientMappers(token, clientId) {
  114. console.log(`Getting client mappers for ${clientId}...`);
  115. const client = await getClientByClientId(token, clientId);
  116. if (!client) {
  117. console.log(`Client ${clientId} not found, no mappers to get.`);
  118. return [];
  119. }
  120. try {
  121. const response = await axios.get(
  122. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}/protocol-mappers/models`,
  123. { headers: { 'Authorization': `Bearer ${token}` } }
  124. );
  125. console.log(`Client mappers for ${clientId} retrieved.`);
  126. return response.data;
  127. } catch (error) {
  128. handleAxiosError(error, `getting client mappers for ${clientId}`, error.config, error.response);
  129. return [];
  130. }
  131. }
  132. // Get client scopes for a client
  133. async function getClientScopes(token, clientId) {
  134. console.log(`Getting client scopes for ${clientId}...`);
  135. const client = await getClientByClientId(token, clientId);
  136. if (!client) {
  137. console.log(`Client ${clientId} not found, no client scopes to get.`);
  138. return [];
  139. }
  140. try {
  141. const response = await axios.get(
  142. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}/client-scopes`,
  143. { headers: { 'Authorization': `Bearer ${token}` } }
  144. );
  145. console.log(`Client scopes for ${clientId} retrieved.`);
  146. return response.data;
  147. } catch (error) {
  148. handleAxiosError(error, `getting client scopes for ${clientId}`, error.config, error.response);
  149. return [];
  150. }
  151. }
  152. // Get a specific client scope by name
  153. async function getClientScope(token, scopeName) {
  154. console.log(`Getting client scope "${scopeName}"...`);
  155. try {
  156. const response = await axios.get(
  157. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes`,
  158. { headers: { 'Authorization': `Bearer ${token}` } }
  159. );
  160. const foundScope = response.data.find(scope => scope.name === scopeName);
  161. if (!foundScope) {
  162. console.log(`Client Scope "${scopeName}" not found`);
  163. return null;
  164. }
  165. console.log(`Client scope "${scopeName}" found:`, foundScope);
  166. return foundScope;
  167. } catch (error) {
  168. handleAxiosError(error, `getting client scope ${scopeName}`, error.config, error.response);
  169. return null;
  170. }
  171. }
  172. // Add a default client scope to a client
  173. async function addDefaultClientScope(token, clientId, scopeName) {
  174. console.log(`Adding client scope "${scopeName}" as default for client "${clientId}"...`);
  175. const client = await getClientByClientId(token, clientId);
  176. const scope = await getClientScope(token, scopeName);
  177. if (!client || !scope) {
  178. console.log(`Client "${clientId}" or scope "${scopeName}" not found, cannot add as default scope.`);
  179. return;
  180. }
  181. try {
  182. await axios.put(
  183. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}/default-client-scopes/${scope.id}`,
  184. null,
  185. {
  186. headers: {
  187. 'Authorization': `Bearer ${token}`,
  188. 'Content-Type': 'application/json'
  189. }
  190. }
  191. );
  192. console.log(`Client scope "${scopeName}" added as default scope for client "${clientId}"`);
  193. } catch (error) {
  194. handleAxiosError(error, `adding client scope "${scopeName}" as default for client "${clientId}"`);
  195. }
  196. }
  197. // Create Realm
  198. async function createRealm(token) {
  199. console.log(`Creating realm ${REALM_NAME}...`);
  200. const realmConfig = {
  201. realm: REALM_NAME,
  202. enabled: true,
  203. displayName: "Office Automation",
  204. displayNameHtml: "<div class=\"kc-logo-text\">Office Automation</div>",
  205. sslRequired: "external",
  206. registrationAllowed: false,
  207. loginWithEmailAllowed: true,
  208. duplicateEmailsAllowed: false,
  209. resetPasswordAllowed: true,
  210. editUsernameAllowed: false,
  211. bruteForceProtected: true,
  212. permanentLockout: false,
  213. maxFailureWaitSeconds: 900,
  214. minimumQuickLoginWaitSeconds: 60,
  215. waitIncrementSeconds: 60,
  216. quickLoginCheckMilliSeconds: 1000,
  217. maxDeltaTimeSeconds: 43200,
  218. failureFactor: 3,
  219. defaultSignatureAlgorithm: "RS256",
  220. offlineSessionMaxLifespan: 5184000,
  221. offlineSessionMaxLifespanEnabled: true,
  222. webAuthnPolicySignatureAlgorithms: ["ES256"],
  223. webAuthnPolicyAttestationConveyancePreference: "none",
  224. webAuthnPolicyAuthenticatorAttachment: "cross-platform",
  225. webAuthnPolicyRequireResidentKey: "not specified",
  226. webAuthnPolicyUserVerificationRequirement: "preferred",
  227. webAuthnPolicyCreateTimeout: 0,
  228. webAuthnPolicyAvoidSameAuthenticatorRegister: false,
  229. defaultDefaultClientScopes: ["email", "profile", "roles", "web-origins"],
  230. defaultOptionalClientScopes: ["address", "phone", "offline_access", "microprofile-jwt"]
  231. };
  232. try {
  233. await axios.post(`${KEYCLOAK_URL}/admin/realms`, realmConfig, {
  234. headers: {
  235. 'Authorization': `Bearer ${token}`,
  236. 'Content-Type': 'application/json'
  237. }
  238. });
  239. console.log('Realm created successfully');
  240. } catch (error) {
  241. handleAxiosError(error, 'creating realm');
  242. }
  243. }
  244. // Create client and manage mappers
  245. async function createClient(token, clientId, clientName, redirectUris) {
  246. console.log(`Creating client "${clientId}"...`);
  247. let client = await getClientByClientId(token, clientId);
  248. if (!client) {
  249. const clientConfig = {
  250. clientId: clientId,
  251. name: clientName,
  252. enabled: true,
  253. protocol: "openid-connect",
  254. publicClient: false,
  255. authorizationServicesEnabled: false,
  256. serviceAccountsEnabled: false,
  257. standardFlowEnabled: true,
  258. implicitFlowEnabled: false,
  259. directAccessGrantsEnabled: true,
  260. redirectUris: redirectUris,
  261. webOrigins: ["+"],
  262. defaultClientScopes: ["roles"],
  263. optionalClientScopes: ["address", "phone", "offline_access", "microprofile-jwt"],
  264. rootUrl: process.env.NEXTCLOUD_URL,
  265. baseUrl: process.env.NEXTCLOUD_URL,
  266. adminUrl: process.env.NEXTCLOUD_URL,
  267. };
  268. try {
  269. const response = await axios.post(
  270. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients`,
  271. clientConfig,
  272. {
  273. headers: {
  274. 'Authorization': `Bearer ${token}`,
  275. 'Content-Type': 'application/json'
  276. }
  277. }
  278. );
  279. console.log(`Client "${clientId}" created successfully`);
  280. client = response.data;
  281. } catch (error) {
  282. handleAxiosError(error, `creating client: ${clientId}`);
  283. return;
  284. }
  285. } else {
  286. console.log(`Client "${clientId}" already exists, checking mappers`);
  287. }
  288. if (client) {
  289. try {
  290. await axios.put(
  291. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
  292. { ...client, secret: process.env[`KEYCLOAK_${clientId.replace(/[^a-zA-Z0-9]/g, '').toUpperCase()}_CLIENT_SECRET`] },
  293. {
  294. headers: {
  295. 'Authorization': `Bearer ${token}`,
  296. 'Content-Type': 'application/json'
  297. }
  298. }
  299. );
  300. console.log(`Set client secret for client: ${clientId}`);
  301. } catch (error) {
  302. handleAxiosError(error, `setting client secret for client: ${clientId}`, error.config, error.response);
  303. }
  304. const existingMappers = await getClientMappers(token, clientId);
  305. const requiredMappers = [
  306. {
  307. name: "groups",
  308. protocol: "openid-connect",
  309. protocolMapper: "oidc-group-membership-mapper",
  310. config: {
  311. "full.path": "true",
  312. "id.token.claim": "true",
  313. "access.token.claim": "true",
  314. "userinfo.token.claim": "true",
  315. "claim.name": "groups"
  316. }
  317. },
  318. {
  319. name: "realm roles",
  320. protocol: "openid-connect",
  321. protocolMapper: "oidc-usermodel-realm-role-mapper",
  322. config: {
  323. "id.token.claim": "true",
  324. "access.token.claim": "true",
  325. "userinfo.token.claim": "true",
  326. "claim.name": "roles",
  327. "jsonType.label": "String",
  328. "multivalued": "true"
  329. }
  330. }
  331. ];
  332. for (const mapper of requiredMappers) {
  333. const existingMapper = existingMappers.find(m => m.name === mapper.name);
  334. try {
  335. if (existingMapper) {
  336. await axios.put(
  337. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}/protocol-mappers/models/${existingMapper.id}`,
  338. { ...existingMapper, ...mapper },
  339. {
  340. headers: {
  341. 'Authorization': `Bearer ${token}`,
  342. 'Content-Type': 'application/json'
  343. }
  344. }
  345. );
  346. console.log(`Mapper "${mapper.name}" updated for client "${clientId}"`);
  347. } else {
  348. await axios.post(
  349. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}/protocol-mappers/models`,
  350. mapper,
  351. {
  352. headers: {
  353. 'Authorization': `Bearer ${token}`,
  354. 'Content-Type': 'application/json'
  355. }
  356. }
  357. );
  358. console.log(`Mapper "${mapper.name}" created for client "${clientId}"`);
  359. }
  360. } catch (error) {
  361. handleAxiosError(error, `managing mapper "${mapper.name}" for client "${clientId}"`, error.config, error.response);
  362. }
  363. }
  364. if (clientId.includes("nextcloud")) {
  365. await addDefaultClientScope(token, clientId, "openid");
  366. await addDefaultClientScope(token, clientId, "profile");
  367. await addDefaultClientScope(token, clientId, "groups-nextcloud");
  368. try {
  369. await axios.put(
  370. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
  371. { ...client, defaultClientScopes: client.defaultClientScopes.filter(c => c !== "nextcloud-dedicated") },
  372. {
  373. headers: {
  374. 'Authorization': `Bearer ${token}`,
  375. 'Content-Type': 'application/json'
  376. }
  377. }
  378. );
  379. console.log(`Removed client scope nextcloud-dedicated from client: ${clientId}`);
  380. } catch (error) {
  381. handleAxiosError(error, `removing client scope nextcloud-dedicated from client: ${clientId}`, error.config, error.response);
  382. }
  383. }
  384. }
  385. }
  386. // Create default groups
  387. async function createDefaultGroups(token) {
  388. console.log('Creating default groups...');
  389. const groups = [
  390. { name: "nextcloud-admins", path: "/nextcloud-admins", attributes: { "description": ["Nextcloud administrators"] } },
  391. { name: "nextcloud-users", path: "/nextcloud-users", attributes: { "description": ["Nextcloud regular users"] } },
  392. { name: "nextcloud-youpi", path: "/nextcloud-youpi", attributes: { "description": ["Nextcloud youpi"] } },
  393. { name: "nextcloud-service", path: "/nextcloud-service", attributes: { "description": ["Nextcloud service user"] } }
  394. ];
  395. for (const group of groups) {
  396. try {
  397. const existingGroups = await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/groups`, {
  398. headers: { 'Authorization': `Bearer ${token}` },
  399. params: { search: group.name, exact: true } // Added exact: true for precise matching
  400. });
  401. if (existingGroups.data.length > 0) {
  402. console.log(`Group "${group.name}" already exists`);
  403. continue;
  404. }
  405. await axios.post(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/groups`, group, {
  406. headers: {
  407. 'Authorization': `Bearer ${token}`,
  408. 'Content-Type': 'application/json'
  409. }
  410. });
  411. console.log(`Group "${group.name}" created successfully`);
  412. } catch (error) {
  413. if (error.response?.status === 409) {
  414. console.log(`Group "${group.name}" already exists`);
  415. } else {
  416. handleAxiosError(error, `creating group: ${group.name}`);
  417. }
  418. }
  419. }
  420. }
  421. // Create a test token for a user
  422. async function createTestToken(token, username) {
  423. console.log(`Creating test token for user "${username}"...`);
  424. try {
  425. const nextcloudClientId = Object.keys(CLIENTS).find(key => key.includes('nextcloud')) || 'nextcloud';
  426. const client = await getClientByClientId(token, nextcloudClientId);
  427. if (!client) {
  428. console.log(`Client "${nextcloudClientId}" not found, cannot create test token.`);
  429. return null;
  430. }
  431. const response = await axios.post(
  432. `${KEYCLOAK_URL}/realms/${REALM_NAME}/protocol/openid-connect/token`,
  433. new URLSearchParams({
  434. 'client_id': nextcloudClientId,
  435. 'client_secret': process.env[`KEYCLOAK_${nextcloudClientId.replace(/[^a-zA-Z0-9]/g, '').toUpperCase()}_CLIENT_SECRET`],
  436. 'username': username,
  437. 'password': process.env.TESTADMIN_PASSWORD || "initial123!",
  438. 'grant_type': 'password'
  439. }),
  440. { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
  441. );
  442. console.log(`Test token for user "${username}" created.`);
  443. return response.data.access_token;
  444. } catch (error) {
  445. handleAxiosError(error, `getting test token for ${username}`, error.config, error.response);
  446. return null;
  447. }
  448. }
  449. // Function to decode a JWT token
  450. function decodeToken(token) {
  451. try {
  452. const base64Url = token.split('.')[1];
  453. const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
  454. const jsonPayload = decodeURIComponent(atob(base64).split('').map(c =>
  455. '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
  456. ).join(''));
  457. return JSON.parse(jsonPayload);
  458. } catch (error) {
  459. console.error("Error decoding token:", error.message);
  460. return null;
  461. }
  462. }
  463. // Create initial users
  464. async function createInitialUsers(token) {
  465. console.log('Creating initial users...');
  466. const users = [
  467. { username: "testadmin", enabled: true, emailVerified: true, firstName: "Test", lastName: "Admin", email: "testadmin@mrx8086.com", credentials: [{ type: "password", value: process.env.TESTADMIN_PASSWORD || "initial123!", temporary: false }], groups: ["nextcloud-admins", "nextcloud-users"] },
  468. { username: "testuser", enabled: true, emailVerified: true, firstName: "Test", lastName: "User", email: "testuser@mrx8086.com", credentials: [{ type: "password", value: process.env.TESTUSER_PASSWORD || "initial123!", temporary: false }], groups: ["nextcloud-users", "nextcloud-youpi"] },
  469. { username: "testserviceuser", enabled: true, emailVerified: true, firstName: "Test", lastName: "Service User", email: "testserviceuser@mrx8086.com", credentials: [{ type: "password", value: process.env.TESTSERVICEUSER_PASSWORD || "initial123!", temporary: false }], groups: ["nextcloud-service"] }
  470. ];
  471. for (const user of users) {
  472. try {
  473. const existingUsers = await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users`, {
  474. headers: { 'Authorization': `Bearer ${token}` },
  475. params: { username: user.username, exact: true } // Added exact: true for precise matching
  476. });
  477. if (existingUsers.data.length > 0) {
  478. console.log(`User "${user.username}" already exists`);
  479. continue;
  480. }
  481. await axios.post(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users`, user, {
  482. headers: {
  483. 'Authorization': `Bearer ${token}`,
  484. 'Content-Type': 'application/json'
  485. }
  486. });
  487. console.log(`User "${user.username}" created successfully`);
  488. } catch (error) {
  489. handleAxiosError(error, `creating user: ${user.username}`, error.config, error.response);
  490. }
  491. }
  492. }
  493. async function createGroupsNextcloudScope(token) {
  494. const scopeName = "groups-nextcloud";
  495. const mapperName = "groups-mapper";
  496. console.log(`Starting createGroupsNextcloudScope`);
  497. let clientScope = await getClientScope(token, scopeName);
  498. if (!clientScope) {
  499. try {
  500. console.log(`Creating client scope "${scopeName}"`);
  501. clientScope = await axios.post(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes`,
  502. {
  503. "name": scopeName,
  504. "protocol": "openid-connect",
  505. "description": "Provides access to user group information for Nextcloud.", // Hinzugefügt: Beschreibung
  506. "attributes": {},
  507. "consentScreenText": "Grant access to user groups in Nextcloud",
  508. "includeInTokenScope": true
  509. },
  510. {
  511. headers: {
  512. 'Authorization': `Bearer ${token}`,
  513. 'Content-Type': 'application/json'
  514. }
  515. });
  516. console.log(`Client scope "${scopeName}" created successfully`);
  517. clientScope = response.data;
  518. } catch (error) {
  519. console.error(`Error creating client scope "${scopeName}":`, error);
  520. handleAxiosError(error, `creating ${scopeName} client scope`, error.config, error.response);
  521. return;
  522. }
  523. } else {
  524. console.log(`Client scope "${scopeName}" exists, skipping creation`);
  525. }
  526. console.log("Client scope creation step finished");
  527. if (clientScope) {
  528. console.log(`Check for mapper "${mapperName}" in scope "${scopeName}"`);
  529. try {
  530. const mappersResponse = await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes/${clientScope.id}/protocol-mappers/models`,
  531. { headers: { 'Authorization': `Bearer ${token}` } }
  532. );
  533. if (!mappersResponse.data.find(m => m.name === mapperName)) {
  534. try {
  535. await axios.post(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes/${clientScope.id}/protocol-mappers/models`,
  536. {
  537. "name": mapperName,
  538. "protocol": "openid-connect",
  539. "protocolMapper": "oidc-group-membership-mapper",
  540. "config": {
  541. "full.path": "false",
  542. "id.token.claim": "false",
  543. "access.token.claim": "true",
  544. "userinfo.token.claim": "true",
  545. "claim.name": "groups",
  546. "add.to.introspection": "false"
  547. }
  548. },
  549. {
  550. headers: {
  551. 'Authorization': `Bearer ${token}`,
  552. 'Content-Type': 'application/json'
  553. }
  554. });
  555. console.log(`Mapper "${mapperName}" created for client scope "${scopeName}"`);
  556. } catch (error) {
  557. console.error(`Error creating mapper "${mapperName}" for scope "${scopeName}":`, error);
  558. handleAxiosError(error, `creating mapper for ${scopeName}`, error.config, error.response);
  559. }
  560. } else {
  561. console.log(`Mapper "${mapperName}" exists in client scope "${scopeName}", skipping creation`);
  562. }
  563. } catch (error) {
  564. console.error("Error checking for mappers:", error);
  565. handleAxiosError(error, `checking mappers for ${scopeName}`, error.config, error.response);
  566. }
  567. }
  568. console.log("Finished createGroupsNextcloudScope");
  569. }
  570. // Main function
  571. async function setupRealm() {
  572. try {
  573. console.log('Starting Keycloak setup...');
  574. const token = await getAdminToken();
  575. // Check if realm exists
  576. const realmExists = await checkRealmExists(token);
  577. if (!realmExists) {
  578. console.log('Creating new realm...');
  579. await createRealm(token);
  580. } else {
  581. console.log('Realm already exists, skipping base setup');
  582. }
  583. // Create client scope groups-nextcloud
  584. await createGroupsNextcloudScope(token);
  585. // Create clients
  586. for (const clientId in CLIENTS) {
  587. await createClient(token, clientId, clientId, CLIENTS[clientId].redirectUris);
  588. }
  589. // Create groups
  590. await createDefaultGroups(token);
  591. // Create test users
  592. await createInitialUsers(token);
  593. // Read the configuration of the Office-Automation realm with Admin Token
  594. if (token) {
  595. console.log("Master Realm Admin Token:", token);
  596. try {
  597. const realmConfig = await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}`, {
  598. headers: { 'Authorization': `Bearer ${token}` }
  599. });
  600. console.log("Office Automation Realm Configuration:", realmConfig.data);
  601. } catch (error) {
  602. handleAxiosError(error, 'getting office realm configuration', error.config, error.response);
  603. }
  604. } else {
  605. console.error("Error getting Master Realm admin token");
  606. }
  607. // Create Test Token
  608. const testToken = await createTestToken(token, "testadmin");
  609. if (testToken) {
  610. console.log("Test Token generated successfully!");
  611. const decodedToken = decodeToken(testToken);
  612. if (decodedToken)
  613. console.log("Token:", decodedToken);
  614. } else {
  615. console.error("Error generating Test Token");
  616. }
  617. console.log('Setup completed successfully');
  618. } catch (error) {
  619. console.error('Setup failed:', error);
  620. process.exit(1);
  621. }
  622. }
  623. // Execute the script
  624. setupRealm();