check_keycloak_client.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. import dotenv from 'dotenv';
  2. import axios from 'axios';
  3. // Load environment variables
  4. dotenv.config();
  5. // Configuration constants
  6. const KEYCLOAK_URL = process.env.KEYCLOAK_URL || 'https://auth.mrx8086.com';
  7. const ADMIN_USERNAME = process.env.KEYCLOAK_ADMIN_USER;
  8. const ADMIN_PASSWORD = process.env.KEYCLOAK_ADMIN_PASSWORD;
  9. const REALM_NAME = 'office-automation';
  10. // Client ID for Paperless
  11. const PAPERLESS_CLIENT_ID = process.env.PAPERLESS_CLIENT_ID || 'paperless';
  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. // Function to get client information by clientId
  58. async function getClient(token, clientId) {
  59. console.log(`Getting client information for ${clientId}...`);
  60. try {
  61. const response = await axios.get(
  62. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients`,
  63. {
  64. headers: {
  65. 'Authorization': `Bearer ${token}`
  66. },
  67. params: {
  68. clientId: clientId
  69. }
  70. }
  71. );
  72. if (response.data.length === 0) {
  73. console.log(`Client ${clientId} not found`);
  74. return null;
  75. }
  76. console.log(`Client ${clientId} found.`);
  77. return response.data[0];
  78. } catch (error) {
  79. handleAxiosError(error, `getting client ${clientId}`);
  80. return null;
  81. }
  82. }
  83. // Function to get client secret
  84. async function getClientSecret(token, clientId) {
  85. console.log(`Getting client secret for ${clientId}...`);
  86. const client = await getClient(token, clientId);
  87. if (!client) {
  88. console.log(`Client ${clientId} not found, no secret to get.`);
  89. return null;
  90. }
  91. try {
  92. const response = await axios.get(
  93. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
  94. {
  95. headers: { 'Authorization': `Bearer ${token}` }
  96. }
  97. );
  98. console.log(`Client secret for ${clientId} retrieved.`);
  99. return response.data.secret;
  100. } catch (error) {
  101. handleAxiosError(error, `getting client secret for ${clientId}`);
  102. return null;
  103. }
  104. }
  105. // Function to get redirect URIs
  106. async function getClientRedirectUris(token, clientId) {
  107. console.log(`Getting redirect URIs for ${clientId}...`);
  108. const client = await getClient(token, clientId);
  109. if (!client) {
  110. console.log(`Client ${clientId} not found, no redirect URIs to get.`);
  111. return null;
  112. }
  113. try {
  114. const response = await axios.get(
  115. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
  116. {
  117. headers: { 'Authorization': `Bearer ${token}` }
  118. }
  119. );
  120. console.log(`Redirect URIs for ${clientId} retrieved.`);
  121. return response.data.redirectUris;
  122. } catch (error) {
  123. handleAxiosError(error, `getting redirect URIs for ${clientId}`);
  124. return null;
  125. }
  126. }
  127. // Function to get default client scopes
  128. async function getDefaultClientScopes(token, clientId) {
  129. console.log(`Getting default client scopes for ${clientId}...`);
  130. const client = await getClient(token, clientId);
  131. if (!client) {
  132. console.log(`Client ${clientId} not found, no default client scopes to get.`);
  133. return null;
  134. }
  135. try {
  136. const response = await axios.get(
  137. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
  138. {
  139. headers: { 'Authorization': `Bearer ${token}` }
  140. }
  141. );
  142. console.log(`Default client scopes for ${clientId} retrieved.`);
  143. return response.data.defaultClientScopes;
  144. } catch (error) {
  145. handleAxiosError(error, `getting default client scopes for ${clientId}`);
  146. return null;
  147. }
  148. }
  149. // Function to get client mappers
  150. async function getClientMappers(token, clientId) {
  151. console.log(`Getting client mappers for ${clientId}...`);
  152. const client = await getClient(token, clientId);
  153. if (!client) {
  154. console.log(`Client ${clientId} not found, no mappers to get.`);
  155. return [];
  156. }
  157. try {
  158. const response = await axios.get(
  159. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}/protocol-mappers/models`,
  160. {
  161. headers: {
  162. 'Authorization': `Bearer ${token}`
  163. }
  164. }
  165. );
  166. console.log(`Client mappers for ${clientId} retrieved.`);
  167. return response.data;
  168. } catch (error) {
  169. handleAxiosError(error, `getting client mappers for ${clientId}`);
  170. return [];
  171. }
  172. }
  173. // Function to update redirect URIs
  174. async function updateClientRedirectUris(token, clientId, redirectUris) {
  175. console.log(`Updating redirect URIs for client ${clientId} to: ${JSON.stringify(redirectUris)}...`);
  176. const client = await getClient(token, clientId);
  177. if (!client) {
  178. console.log(`Client ${clientId} not found, cannot update redirect URIs.`);
  179. return;
  180. }
  181. try {
  182. await axios.put(
  183. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
  184. { ...client, redirectUris: redirectUris },
  185. {
  186. headers: {
  187. 'Authorization': `Bearer ${token}`,
  188. 'Content-Type': 'application/json'
  189. }
  190. }
  191. );
  192. console.log(`Redirect URIs for client ${clientId} updated successfully.`);
  193. } catch (error) {
  194. handleAxiosError(error, `updating redirect URIs for client ${clientId}`);
  195. }
  196. }
  197. // Function to update default client scopes
  198. async function updateDefaultClientScopes(token, clientId, defaultClientScopes) {
  199. console.log(`Updating default client scopes for client ${clientId} to: ${JSON.stringify(defaultClientScopes)}...`);
  200. const client = await getClient(token, clientId);
  201. if (!client) {
  202. console.log(`Client ${clientId} not found, cannot update default client scopes.`);
  203. return;
  204. }
  205. try {
  206. await axios.put(
  207. `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
  208. { ...client, defaultClientScopes: defaultClientScopes },
  209. {
  210. headers: {
  211. 'Authorization': `Bearer ${token}`,
  212. 'Content-Type': 'application/json'
  213. }
  214. }
  215. );
  216. console.log(`Default client scopes for client ${clientId} updated successfully.`);
  217. } catch (error) {
  218. handleAxiosError(error, `updating default client scopes for client ${clientId}`);
  219. }
  220. }
  221. // Main function to check client configuration
  222. async function checkClientConfig(token, clientId) {
  223. console.log(`Checking configuration for client ${clientId}...`);
  224. const clientSecret = await getClientSecret(token, clientId);
  225. const redirectUris = await getClientRedirectUris(token, clientId);
  226. const defaultClientScopes = await getDefaultClientScopes(token, clientId);
  227. const mappers = await getClientMappers(token, clientId);
  228. console.log(`Client Secret: ${clientSecret}`);
  229. console.log(`Redirect URIs: ${JSON.stringify(redirectUris)}`);
  230. console.log(`Default Client Scopes: ${JSON.stringify(defaultClientScopes)}`);
  231. console.log("Mappers:");
  232. mappers.forEach(mapper => console.log(` - name: ${mapper.name}, type: ${mapper.protocolMapper}`));
  233. // Check and update redirect URIs
  234. const expectedRedirectUris = ["https://docs.mrx8086.com/*"];
  235. if (JSON.stringify(redirectUris) !== JSON.stringify(expectedRedirectUris)) {
  236. console.log(`Redirect URIs do not match expected values. Updating...`);
  237. await updateClientRedirectUris(token, clientId, expectedRedirectUris);
  238. }
  239. // Check and update default client scopes
  240. const expectedDefaultClientScopes = ["profile", "groups-paperless"];
  241. if (JSON.stringify(defaultClientScopes) !== JSON.stringify(expectedDefaultClientScopes)) {
  242. console.log(`Default client scopes do not match expected values. Updating...`);
  243. await updateDefaultClientScopes(token, clientId, expectedDefaultClientScopes);
  244. }
  245. console.log(`Finished checking configuration for client ${clientId}.`);
  246. }
  247. // Main function
  248. async function main() {
  249. try {
  250. console.log('Starting Keycloak client configuration check...');
  251. const token = await getAdminToken();
  252. await checkClientConfig(token, PAPERLESS_CLIENT_ID);
  253. console.log('Keycloak client configuration check completed successfully.');
  254. } catch (error) {
  255. console.error('Keycloak client configuration check failed:', error);
  256. process.exit(1);
  257. }
  258. }
  259. // Execute the script
  260. main();