import dotenv from 'dotenv'; import axios from 'axios'; // Load environment variables dotenv.config(); console.log('Environment variables loaded.'); // Configuration constants const KEYCLOAK_URL = process.env.KEYCLOAK_URL || 'https://auth.mrx8086.com'; const ADMIN_USERNAME = process.env.KEYCLOAK_ADMIN_USER; const ADMIN_PASSWORD = process.env.KEYCLOAK_ADMIN_PASSWORD; const REALM_NAME = 'office-automation'; console.log('Configuration constants set:', { KEYCLOAK_URL, ADMIN_USERNAME, REALM_NAME }); // Helper function for API error handling const handleAxiosError = (error, operation, config, response) => { console.error(`Error during ${operation}:`); if (config) { console.error('Request:', { method: config.method, url: config.url, headers: config.headers, data: config.data, }); } if (error.response) { console.error('Response:', { status: error.response.status, data: error.response.data }); } else { console.error('Error Message:', error.message); } throw error; }; // Get Admin Token async function getAdminToken() { console.log('Getting admin token...'); try { const response = await axios.post( `${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token`, new URLSearchParams({ 'client_id': 'admin-cli', 'username': ADMIN_USERNAME, 'password': ADMIN_PASSWORD, 'grant_type': 'password' }), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } } ); console.log('Admin token received.'); return response.data.access_token; } catch (error) { handleAxiosError(error, 'getting admin token'); } } async function getClientScope(token, scopeName) { console.log(`Getting client scope "${scopeName}"...`); try { const response = await axios.get( `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes`, { headers: { 'Authorization': `Bearer ${token}` }, params: { name: scopeName } } ); if (response.data.length === 0) { console.log(`Client Scope "${scopeName}" not found.`); return null; } console.log(`Client scope "${scopeName}" found:`, response.data); return response.data[0]; } catch (error) { handleAxiosError(error, `getting client scope "${scopeName}"`, error.config, error.response); return null; } } async function createClientScope(token, scopeName, consentScreenText) { console.log(`Attempting to create client scope "${scopeName}"...`); try { const response = await axios.post(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes`, { "name": scopeName, "protocol": "openid-connect", "attributes": {}, "consentScreenText": consentScreenText, "includeInTokenScope": true }, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }); console.log(`Client scope "${scopeName}" created successfully`); return response.data; } catch (error) { console.error(`Error creating client scope "${scopeName}":`, error); handleAxiosError(error, `creating ${scopeName} client scope`, error.config, error.response); return null; } } async function createMapper(token, clientScopeId, mapperName) { console.log(`Attempting to create mapper "${mapperName}" for client scope ID "${clientScopeId}"...`); try { const response = await axios.post(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes/${clientScopeId}/protocol-mappers/models`, { "name": mapperName, "protocol": "openid-connect", "protocolMapper": "oidc-group-membership-mapper", "config": { "full.path": "false", "id.token.claim": "false", "access.token.claim": "true", "userinfo.token.claim": "true", "claim.name": "groups", "add.to.introspection": "false" } }, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }); console.log(`Mapper "${mapperName}" created for client scope ID "${clientScopeId}"`); return response.data; } catch (error) { console.error(`Error creating mapper "${mapperName}" for client scope ID "${clientScopeId}":`, error); handleAxiosError(error, `creating mapper "${mapperName}"`, error.config, error.response); return null; } } async function createGroupsNextcloudScope(token) { const scopeName = "groups-nextcloud-test"; const mapperName = "groups-mapper-test"; console.log("Starting createGroupsNextcloudScope"); let clientScope = await getClientScope(token, scopeName); if (!clientScope) { clientScope = await createClientScope(token, scopeName, "Grant access to user groups in nextcloud"); } else { console.log(`Client scope "${scopeName}" exists, skipping creation. Details:`, clientScope); } if (clientScope) { console.log("Check for mappers in groups-nextcloud-test scope"); try { const mappersResponse = await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes/${clientScope.id}/protocol-mappers/models`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!mappersResponse.data.find(m => m.name === mapperName)) { await createMapper(token, clientScope.id, mapperName); } else { console.log(`Mapper "${mapperName}" exists, skipping creation. Details:`, mappersResponse.data.find(m => m.name === mapperName)); } } catch (error) { console.error("Error checking for mappers:", error); handleAxiosError(error, `checking mappers for ${scopeName}`, error.config, error.response); } } console.log("Finished createGroupsNextcloudScope"); } async function createSimpleTestScope(token) { const uniqueSuffix = Date.now(); const simpleScopeName = `test-simple-scope-${uniqueSuffix}`; console.log(`Attempting to create simple test scope with name: ${simpleScopeName}`); const simpleScope = await createClientScope(token, simpleScopeName, "Simple test scope"); if (simpleScope) { console.log(`Successfully created simple test scope:`, simpleScope); } } // Main function async function setupRealm() { try { console.log('Starting Keycloak setup...'); const token = await getAdminToken(); // Create a simple test client scope await createSimpleTestScope(token); // Create client scope groups-nextcloud await createGroupsNextcloudScope(token); console.log('Setup completed successfully'); } catch (error) { console.error('Setup failed:', error); process.exit(1); } } // Execute the script setupRealm();