UNPKG

@logicpanel/keycloak-admin-client

Version:
1,556 lines (1,509 loc) 86 kB
'use strict'; function normalize (strArray) { var resultArray = []; if (strArray.length === 0) { return ''; } if (typeof strArray[0] !== 'string') { throw new TypeError('Url must be a string. Received ' + strArray[0]); } // If the first part is a plain protocol, we combine it with the next part. if (strArray[0].match(/^[^/:]+:\/*$/) && strArray.length > 1) { var first = strArray.shift(); strArray[0] = first + strArray[0]; } // There must be two or three slashes in the file protocol, two slashes in anything else. if (strArray[0].match(/^file:\/\/\//)) { strArray[0] = strArray[0].replace(/^([^/:]+):\/*/, '$1:///'); } else { strArray[0] = strArray[0].replace(/^([^/:]+):\/*/, '$1://'); } for (var i = 0; i < strArray.length; i++) { var component = strArray[i]; if (typeof component !== 'string') { throw new TypeError('Url must be a string. Received ' + component); } if (component === '') { continue; } if (i > 0) { // Removing the starting slashes for each component but the first. component = component.replace(/^[\/]+/, ''); } if (i < strArray.length - 1) { // Removing the ending slashes for each component but the last. component = component.replace(/[\/]+$/, ''); } else { // For the last component we will combine multiple slashes to a single one. component = component.replace(/[\/]+$/, '/'); } resultArray.push(component); } var str = resultArray.join('/'); // Each input component is now separated by a single slash except the possible first plain protocol part. // remove trailing slash before parameters or hash str = str.replace(/\/(\?|&|#[^!])/g, '$1'); // replace ? in parameters with & var parts = str.split('?'); str = parts.shift() + (parts.length > 0 ? '?': '') + parts.join('&'); return str; } function urlJoin() { var input; if (typeof arguments[0] === 'object') { input = arguments[0]; } else { input = [].slice.call(arguments); } return normalize(input); } function encodeReserved(str) { return str.split(/(%[0-9A-Fa-f]{2})/g).map(function (part) { if (!/%[0-9A-Fa-f]/.test(part)) { part = encodeURI(part).replace(/%5B/g, '[').replace(/%5D/g, ']'); } return part; }).join(''); } function encodeUnreserved(str) { return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { return '%' + c.charCodeAt(0).toString(16).toUpperCase(); }); } function encodeValue(operator, value, key) { value = (operator === '+' || operator === '#') ? encodeReserved(value) : encodeUnreserved(value); if (key) { return encodeUnreserved(key) + '=' + value; } else { return value; } } function isDefined(value) { return value !== undefined && value !== null; } function isKeyOperator(operator) { return operator === ';' || operator === '&' || operator === '?'; } function getValues(context, operator, key, modifier) { var value = context[key], result = []; if (isDefined(value) && value !== '') { if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { value = value.toString(); if (modifier && modifier !== '*') { value = value.substring(0, parseInt(modifier, 10)); } result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : null)); } else { if (modifier === '*') { if (Array.isArray(value)) { value.filter(isDefined).forEach(function (value) { result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : null)); }); } else { Object.keys(value).forEach(function (k) { if (isDefined(value[k])) { result.push(encodeValue(operator, value[k], k)); } }); } } else { var tmp = []; if (Array.isArray(value)) { value.filter(isDefined).forEach(function (value) { tmp.push(encodeValue(operator, value)); }); } else { Object.keys(value).forEach(function (k) { if (isDefined(value[k])) { tmp.push(encodeUnreserved(k)); tmp.push(encodeValue(operator, value[k].toString())); } }); } if (isKeyOperator(operator)) { result.push(encodeUnreserved(key) + '=' + tmp.join(',')); } else if (tmp.length !== 0) { result.push(tmp.join(',')); } } } } else { if (operator === ';') { if (isDefined(value)) { result.push(encodeUnreserved(key)); } } else if (value === '' && (operator === '&' || operator === '?')) { result.push(encodeUnreserved(key) + '='); } else if (value === '') { result.push(''); } } return result; } function parseTemplate(template) { var operators = ['+', '#', '.', '/', ';', '?', '&']; return { expand: function (context) { return template.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g, function (_, expression, literal) { if (expression) { var operator = null, values = []; if (operators.indexOf(expression.charAt(0)) !== -1) { operator = expression.charAt(0); expression = expression.substr(1); } expression.split(/,/g).forEach(function (variable) { var tmp = /([^:\*]*)(?::(\d+)|(\*))?/.exec(variable); values.push.apply(values, getValues(context, operator, tmp[1], tmp[2] || tmp[3])); }); if (operator && operator !== '+') { var separator = ','; if (operator === '?') { separator = '&'; } else if (operator !== '#') { separator = operator; } return (values.length !== 0 ? operator : '') + values.join(separator); } else { return values.join(','); } } else { return encodeReserved(literal); } }); } }; } class NetworkError extends Error { response; responseData; constructor(message, options) { super(message); this.response = options.response; this.responseData = options.responseData; } } async function fetchWithError(input, init) { const response = await fetch(input, init); if (!response.ok) { const responseData = await parseResponse(response); throw new NetworkError("Network response was not OK.", { response, responseData, }); } return response; } async function parseResponse(response) { if (!response.body) { return ""; } const data = await response.text(); try { return JSON.parse(data); // eslint-disable-next-line no-empty } catch (error) { } return data; } function stringifyQueryParams(params) { const searchParams = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { // Ignore undefined and null values. if (value === undefined || value === null) { continue; } // Ignore empty strings. if (typeof value === "string" && value.length === 0) { continue; } // Ignore empty arrays. if (Array.isArray(value) && value.length === 0) { continue; } // Append each entry of an array as a separate parameter, or the value itself otherwise. if (Array.isArray(value)) { value.forEach((item) => searchParams.append(key, item.toString())); } else { searchParams.append(key, value.toString()); } } return searchParams.toString(); } // constants const SLASH = "/"; const pick = (value, keys) => Object.fromEntries(Object.entries(value).filter(([key]) => keys.includes(key))); const omit = (value, keys) => Object.fromEntries(Object.entries(value).filter(([key]) => !keys.includes(key))); class Agent { #client; #basePath; #getBaseParams; #getBaseUrl; constructor({ client, path = "/", getUrlParams = () => ({}), getBaseUrl = () => client.baseUrl, }) { this.#client = client; this.#getBaseParams = getUrlParams; this.#getBaseUrl = getBaseUrl; this.#basePath = path; } request({ method, path = "", urlParamKeys = [], queryParamKeys = [], catchNotFound = false, keyTransform, payloadKey, returnResourceIdInLocationHeader, ignoredKeys, headers, }) { return async (payload = {}, options) => { const baseParams = this.#getBaseParams?.() ?? {}; // Filter query parameters by queryParamKeys const queryParams = queryParamKeys.length > 0 ? pick(payload, queryParamKeys) : undefined; // Add filtered payload parameters to base parameters const allUrlParamKeys = [...Object.keys(baseParams), ...urlParamKeys]; const urlParams = { ...baseParams, ...pick(payload, allUrlParamKeys) }; if (!(payload instanceof FormData)) { // Omit url parameters and query parameters from payload const omittedKeys = ignoredKeys ? [...allUrlParamKeys, ...queryParamKeys].filter((key) => !ignoredKeys.includes(key)) : [...allUrlParamKeys, ...queryParamKeys]; payload = omit(payload, omittedKeys); } // Transform keys of both payload and queryParams if (keyTransform) { this.#transformKey(payload, keyTransform); this.#transformKey(queryParams, keyTransform); } return this.#requestWithParams({ method, path, payload, urlParams, queryParams, // catchNotFound precedence: global > local > default catchNotFound, ...(this.#client.getGlobalRequestArgOptions() ?? options ?? {}), payloadKey, returnResourceIdInLocationHeader, headers, }); }; } updateRequest({ method, path = "", urlParamKeys = [], queryParamKeys = [], catchNotFound = false, keyTransform, payloadKey, returnResourceIdInLocationHeader, headers, }) { return async (query = {}, payload = {}) => { const baseParams = this.#getBaseParams?.() ?? {}; // Filter query parameters by queryParamKeys const queryParams = queryParamKeys ? pick(query, queryParamKeys) : undefined; // Add filtered query parameters to base parameters const allUrlParamKeys = [...Object.keys(baseParams), ...urlParamKeys]; const urlParams = { ...baseParams, ...pick(query, allUrlParamKeys), }; // Transform keys of queryParams if (keyTransform) { this.#transformKey(queryParams, keyTransform); } return this.#requestWithParams({ method, path, payload, urlParams, queryParams, catchNotFound, payloadKey, returnResourceIdInLocationHeader, headers, }); }; } async #requestWithParams({ method, path, payload, urlParams, queryParams, catchNotFound, payloadKey, returnResourceIdInLocationHeader, headers, }) { const newPath = urlJoin(this.#basePath, path); // Parse template and replace with values from urlParams const pathTemplate = parseTemplate(newPath); const parsedPath = pathTemplate.expand(urlParams); const url = new URL(`${this.#getBaseUrl?.() ?? ""}${parsedPath}`); const requestOptions = { ...this.#client.getRequestOptions() }; const requestHeaders = new Headers([ ...new Headers(requestOptions.headers).entries(), ["authorization", `Bearer ${await this.#client.getAccessToken()}`], ["accept", "application/json, text/plain, */*"], ...new Headers(headers).entries(), ]); const searchParams = {}; // Add payload parameters to search params if method is 'GET'. if (method === "GET") { Object.assign(searchParams, payload); } else if (requestHeaders.get("content-type") === "text/plain") { // Pass the payload as a plain string if the content type is 'text/plain'. requestOptions.body = payload; } else if (payload instanceof FormData) { requestOptions.body = payload; } else { // Otherwise assume it's JSON and stringify it. requestOptions.body = JSON.stringify(payloadKey ? payload[payloadKey] : payload); } if (!requestHeaders.has("content-type") && !(payload instanceof FormData)) { requestHeaders.set("content-type", "application/json"); } if (queryParams) { Object.assign(searchParams, queryParams); } url.search = stringifyQueryParams(searchParams); try { const res = await fetchWithError(url, { ...requestOptions, headers: requestHeaders, method, }); // now we get the response of the http request // if `resourceIdInLocationHeader` is true, we'll get the resourceId from the location header field // todo: find a better way to find the id in path, maybe some kind of pattern matching // for now, we simply split the last sub-path of the path returned in location header field if (returnResourceIdInLocationHeader) { const locationHeader = res.headers.get("location"); if (typeof locationHeader !== "string") { throw new Error(`location header is not found in request: ${res.url}`); } const resourceId = locationHeader.split(SLASH).pop(); if (!resourceId) { // throw an error to let users know the response is not expected throw new Error(`resourceId is not found in Location header from request: ${res.url}`); } // return with format {[field]: string} const { field } = returnResourceIdInLocationHeader; return { [field]: resourceId }; } if (Object.entries(headers || []).find(([key, value]) => key.toLowerCase() === "accept" && value === "application/octet-stream")) { return res.arrayBuffer(); } return parseResponse(res); } catch (err) { if (err instanceof NetworkError && err.response.status === 404 && catchNotFound) { return null; } throw err; } } #transformKey(payload, keyMapping) { if (!payload) { return; } Object.keys(keyMapping).some((key) => { if (typeof payload[key] === "undefined") { return false; } const newKey = keyMapping[key]; payload[newKey] = payload[key]; delete payload[key]; }); } } class Resource { #agent; constructor(client, settings = {}) { this.#agent = new Agent({ client, ...settings, }); } makeRequest = (args) => { return this.#agent.request(args); }; // update request will take three types: query, payload and response makeUpdateRequest = (args) => { return this.#agent.updateRequest(args); }; } class AttackDetection extends Resource { findOne = this.makeRequest({ method: "GET", path: "/users/{id}", urlParamKeys: ["id"], catchNotFound: true, }); del = this.makeRequest({ method: "DELETE", path: "/users/{id}", urlParamKeys: ["id"], }); delAll = this.makeRequest({ method: "DELETE", path: "/users", }); constructor(client) { super(client, { path: "/admin/realms/{realm}/attack-detection/brute-force", getUrlParams: () => ({ realm: client.realmName, }), getBaseUrl: () => client.baseUrl, }); } } class AuthenticationManagement extends Resource { /** * Authentication Management * https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authentication_management_resource */ // Register a new required action registerRequiredAction = this.makeRequest({ method: "POST", path: "/register-required-action", }); // Get required actions. Returns a list of required actions. getRequiredActions = this.makeRequest({ method: "GET", path: "/required-actions", }); // Get required action for alias getRequiredActionForAlias = this.makeRequest({ method: "GET", path: "/required-actions/{alias}", urlParamKeys: ["alias"], catchNotFound: true, }); getClientAuthenticatorProviders = this.makeRequest({ method: "GET", path: "/client-authenticator-providers", }); getAuthenticatorProviders = this.makeRequest({ method: "GET", path: "/authenticator-providers", }); getFormActionProviders = this.makeRequest({ method: "GET", path: "/form-action-providers", }); // Update required action updateRequiredAction = this.makeUpdateRequest({ method: "PUT", path: "/required-actions/{alias}", urlParamKeys: ["alias"], }); // Delete required action deleteRequiredAction = this.makeRequest({ method: "DELETE", path: "/required-actions/{alias}", urlParamKeys: ["alias"], }); // Lower required action’s priority lowerRequiredActionPriority = this.makeRequest({ method: "POST", path: "/required-actions/{alias}/lower-priority", urlParamKeys: ["alias"], }); // Raise required action’s priority raiseRequiredActionPriority = this.makeRequest({ method: "POST", path: "/required-actions/{alias}/raise-priority", urlParamKeys: ["alias"], }); // Get unregistered required actions Returns a list of unregistered required actions. getUnregisteredRequiredActions = this.makeRequest({ method: "GET", path: "/unregistered-required-actions", }); getFlows = this.makeRequest({ method: "GET", path: "/flows", }); getFlow = this.makeRequest({ method: "GET", path: "/flows/{flowId}", urlParamKeys: ["flowId"], }); getFormProviders = this.makeRequest({ method: "GET", path: "/form-providers", }); createFlow = this.makeRequest({ method: "POST", path: "/flows", returnResourceIdInLocationHeader: { field: "id" }, }); copyFlow = this.makeRequest({ method: "POST", path: "/flows/{flow}/copy", urlParamKeys: ["flow"], }); deleteFlow = this.makeRequest({ method: "DELETE", path: "/flows/{flowId}", urlParamKeys: ["flowId"], }); updateFlow = this.makeUpdateRequest({ method: "PUT", path: "/flows/{flowId}", urlParamKeys: ["flowId"], }); getExecutions = this.makeRequest({ method: "GET", path: "/flows/{flow}/executions", urlParamKeys: ["flow"], }); addExecution = this.makeUpdateRequest({ method: "POST", path: "/flows/{flow}/executions", urlParamKeys: ["flow"], }); addExecutionToFlow = this.makeRequest({ method: "POST", path: "/flows/{flow}/executions/execution", urlParamKeys: ["flow"], returnResourceIdInLocationHeader: { field: "id" }, }); addFlowToFlow = this.makeRequest({ method: "POST", path: "/flows/{flow}/executions/flow", urlParamKeys: ["flow"], returnResourceIdInLocationHeader: { field: "id" }, }); updateExecution = this.makeUpdateRequest({ method: "PUT", path: "/flows/{flow}/executions", urlParamKeys: ["flow"], }); delExecution = this.makeRequest({ method: "DELETE", path: "/executions/{id}", urlParamKeys: ["id"], }); lowerPriorityExecution = this.makeRequest({ method: "POST", path: "/executions/{id}/lower-priority", urlParamKeys: ["id"], }); raisePriorityExecution = this.makeRequest({ method: "POST", path: "/executions/{id}/raise-priority", urlParamKeys: ["id"], }); getConfigDescription = this.makeRequest({ method: "GET", path: "config-description/{providerId}", urlParamKeys: ["providerId"], }); createConfig = this.makeRequest({ method: "POST", path: "/executions/{id}/config", urlParamKeys: ["id"], returnResourceIdInLocationHeader: { field: "id" }, }); updateConfig = this.makeRequest({ method: "PUT", path: "/config/{id}", urlParamKeys: ["id"], }); getConfig = this.makeRequest({ method: "GET", path: "/config/{id}", urlParamKeys: ["id"], }); delConfig = this.makeRequest({ method: "DELETE", path: "/config/{id}", urlParamKeys: ["id"], }); constructor(client) { super(client, { path: "/admin/realms/{realm}/authentication", getUrlParams: () => ({ realm: client.realmName, }), getBaseUrl: () => client.baseUrl, }); } } class Cache extends Resource { clearUserCache = this.makeRequest({ method: "POST", path: "/clear-user-cache", }); constructor(client) { super(client, { path: "/admin/realms/{realm}", getUrlParams: () => ({ realm: client.realmName, }), getBaseUrl: () => client.baseUrl, }); } } /** * https://www.keycloak.org/docs-api/15.0/rest-api/#_client_registration_policy_resource */ class ClientPolicies extends Resource { constructor(client) { super(client, { path: "/admin/realms/{realm}/client-policies", getUrlParams: () => ({ realm: client.realmName, }), getBaseUrl: () => client.baseUrl, }); } /* Client Profiles */ listProfiles = this.makeRequest({ method: "GET", path: "/profiles", queryParamKeys: ["include-global-profiles"], keyTransform: { includeGlobalProfiles: "include-global-profiles", }, }); createProfiles = this.makeRequest({ method: "PUT", path: "/profiles", }); /* Client Policies */ listPolicies = this.makeRequest({ method: "GET", path: "/policies", }); updatePolicy = this.makeRequest({ method: "PUT", path: "/policies", }); } class Clients extends Resource { find = this.makeRequest({ method: "GET", }); create = this.makeRequest({ method: "POST", returnResourceIdInLocationHeader: { field: "id" }, }); /** * Single client */ findOne = this.makeRequest({ method: "GET", path: "/{id}", urlParamKeys: ["id"], catchNotFound: true, }); update = this.makeUpdateRequest({ method: "PUT", path: "/{id}", urlParamKeys: ["id"], }); del = this.makeRequest({ method: "DELETE", path: "/{id}", urlParamKeys: ["id"], }); /** * Client roles */ createRole = this.makeRequest({ method: "POST", path: "/{id}/roles", urlParamKeys: ["id"], returnResourceIdInLocationHeader: { field: "roleName" }, }); listRoles = this.makeRequest({ method: "GET", path: "/{id}/roles", urlParamKeys: ["id"], }); findRole = this.makeRequest({ method: "GET", path: "/{id}/roles/{roleName}", urlParamKeys: ["id", "roleName"], catchNotFound: true, }); updateRole = this.makeUpdateRequest({ method: "PUT", path: "/{id}/roles/{roleName}", urlParamKeys: ["id", "roleName"], }); delRole = this.makeRequest({ method: "DELETE", path: "/{id}/roles/{roleName}", urlParamKeys: ["id", "roleName"], }); findUsersWithRole = this.makeRequest({ method: "GET", path: "/{id}/roles/{roleName}/users", urlParamKeys: ["id", "roleName"], }); /** * Service account user */ getServiceAccountUser = this.makeRequest({ method: "GET", path: "/{id}/service-account-user", urlParamKeys: ["id"], }); /** * Client secret */ generateNewClientSecret = this.makeRequest({ method: "POST", path: "/{id}/client-secret", urlParamKeys: ["id"], }); invalidateSecret = this.makeRequest({ method: "DELETE", path: "/{id}/client-secret/rotated", urlParamKeys: ["id"], }); generateRegistrationAccessToken = this.makeRequest({ method: "POST", path: "/{id}/registration-access-token", urlParamKeys: ["id"], }); getClientSecret = this.makeRequest({ method: "GET", path: "/{id}/client-secret", urlParamKeys: ["id"], }); /** * Client Scopes */ listDefaultClientScopes = this.makeRequest({ method: "GET", path: "/{id}/default-client-scopes", urlParamKeys: ["id"], }); addDefaultClientScope = this.makeRequest({ method: "PUT", path: "/{id}/default-client-scopes/{clientScopeId}", urlParamKeys: ["id", "clientScopeId"], }); delDefaultClientScope = this.makeRequest({ method: "DELETE", path: "/{id}/default-client-scopes/{clientScopeId}", urlParamKeys: ["id", "clientScopeId"], }); listOptionalClientScopes = this.makeRequest({ method: "GET", path: "/{id}/optional-client-scopes", urlParamKeys: ["id"], }); addOptionalClientScope = this.makeRequest({ method: "PUT", path: "/{id}/optional-client-scopes/{clientScopeId}", urlParamKeys: ["id", "clientScopeId"], }); delOptionalClientScope = this.makeRequest({ method: "DELETE", path: "/{id}/optional-client-scopes/{clientScopeId}", urlParamKeys: ["id", "clientScopeId"], }); /** * Protocol Mappers */ addMultipleProtocolMappers = this.makeUpdateRequest({ method: "POST", path: "/{id}/protocol-mappers/add-models", urlParamKeys: ["id"], }); addProtocolMapper = this.makeUpdateRequest({ method: "POST", path: "/{id}/protocol-mappers/models", urlParamKeys: ["id"], }); listProtocolMappers = this.makeRequest({ method: "GET", path: "/{id}/protocol-mappers/models", urlParamKeys: ["id"], }); findProtocolMapperById = this.makeRequest({ method: "GET", path: "/{id}/protocol-mappers/models/{mapperId}", urlParamKeys: ["id", "mapperId"], catchNotFound: true, }); findProtocolMappersByProtocol = this.makeRequest({ method: "GET", path: "/{id}/protocol-mappers/protocol/{protocol}", urlParamKeys: ["id", "protocol"], catchNotFound: true, }); updateProtocolMapper = this.makeUpdateRequest({ method: "PUT", path: "/{id}/protocol-mappers/models/{mapperId}", urlParamKeys: ["id", "mapperId"], }); delProtocolMapper = this.makeRequest({ method: "DELETE", path: "/{id}/protocol-mappers/models/{mapperId}", urlParamKeys: ["id", "mapperId"], }); /** * Scope Mappings */ listScopeMappings = this.makeRequest({ method: "GET", path: "/{id}/scope-mappings", urlParamKeys: ["id"], }); addClientScopeMappings = this.makeUpdateRequest({ method: "POST", path: "/{id}/scope-mappings/clients/{client}", urlParamKeys: ["id", "client"], }); listClientScopeMappings = this.makeRequest({ method: "GET", path: "/{id}/scope-mappings/clients/{client}", urlParamKeys: ["id", "client"], }); listAvailableClientScopeMappings = this.makeRequest({ method: "GET", path: "/{id}/scope-mappings/clients/{client}/available", urlParamKeys: ["id", "client"], }); listCompositeClientScopeMappings = this.makeRequest({ method: "GET", path: "/{id}/scope-mappings/clients/{client}/composite", urlParamKeys: ["id", "client"], }); delClientScopeMappings = this.makeUpdateRequest({ method: "DELETE", path: "/{id}/scope-mappings/clients/{client}", urlParamKeys: ["id", "client"], }); evaluatePermission = this.makeRequest({ method: "GET", path: "/{id}/evaluate-scopes/scope-mappings/{roleContainer}/{type}", urlParamKeys: ["id", "roleContainer", "type"], queryParamKeys: ["scope"], }); evaluateListProtocolMapper = this.makeRequest({ method: "GET", path: "/{id}/evaluate-scopes/protocol-mappers", urlParamKeys: ["id"], queryParamKeys: ["scope"], }); evaluateGenerateAccessToken = this.makeRequest({ method: "GET", path: "/{id}/evaluate-scopes/generate-example-access-token", urlParamKeys: ["id"], queryParamKeys: ["scope", "userId"], }); evaluateGenerateUserInfo = this.makeRequest({ method: "GET", path: "/{id}/evaluate-scopes/generate-example-userinfo", urlParamKeys: ["id"], queryParamKeys: ["scope", "userId"], }); evaluateGenerateIdToken = this.makeRequest({ method: "GET", path: "/{id}/evaluate-scopes/generate-example-id-token", urlParamKeys: ["id"], queryParamKeys: ["scope", "userId"], }); addRealmScopeMappings = this.makeUpdateRequest({ method: "POST", path: "/{id}/scope-mappings/realm", urlParamKeys: ["id", "client"], }); listRealmScopeMappings = this.makeRequest({ method: "GET", path: "/{id}/scope-mappings/realm", urlParamKeys: ["id"], }); listAvailableRealmScopeMappings = this.makeRequest({ method: "GET", path: "/{id}/scope-mappings/realm/available", urlParamKeys: ["id"], }); listCompositeRealmScopeMappings = this.makeRequest({ method: "GET", path: "/{id}/scope-mappings/realm/composite", urlParamKeys: ["id"], }); delRealmScopeMappings = this.makeUpdateRequest({ method: "DELETE", path: "/{id}/scope-mappings/realm", urlParamKeys: ["id"], }); /** * Sessions */ listSessions = this.makeRequest({ method: "GET", path: "/{id}/user-sessions", urlParamKeys: ["id"], }); listOfflineSessions = this.makeRequest({ method: "GET", path: "/{id}/offline-sessions", urlParamKeys: ["id"], }); getSessionCount = this.makeRequest({ method: "GET", path: "/{id}/session-count", urlParamKeys: ["id"], }); /** * Resource */ getResourceServer = this.makeRequest({ method: "GET", path: "{id}/authz/resource-server", urlParamKeys: ["id"], }); updateResourceServer = this.makeUpdateRequest({ method: "PUT", path: "{id}/authz/resource-server", urlParamKeys: ["id"], }); listResources = this.makeRequest({ method: "GET", path: "{id}/authz/resource-server/resource", urlParamKeys: ["id"], }); createResource = this.makeUpdateRequest({ method: "POST", path: "{id}/authz/resource-server/resource", urlParamKeys: ["id"], }); getResource = this.makeRequest({ method: "GET", path: "{id}/authz/resource-server/resource/{resourceId}", urlParamKeys: ["id", "resourceId"], }); updateResource = this.makeUpdateRequest({ method: "PUT", path: "/{id}/authz/resource-server/resource/{resourceId}", urlParamKeys: ["id", "resourceId"], }); delResource = this.makeRequest({ method: "DELETE", path: "/{id}/authz/resource-server/resource/{resourceId}", urlParamKeys: ["id", "resourceId"], }); importResource = this.makeUpdateRequest({ method: "POST", path: "/{id}/authz/resource-server/import", urlParamKeys: ["id"], }); exportResource = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/settings", urlParamKeys: ["id"], }); evaluateResource = this.makeUpdateRequest({ method: "POST", path: "{id}/authz/resource-server/policy/evaluate", urlParamKeys: ["id"], }); /** * Policy */ listPolicies = this.makeRequest({ method: "GET", path: "{id}/authz/resource-server/policy", urlParamKeys: ["id"], }); findPolicyByName = this.makeRequest({ method: "GET", path: "{id}/authz/resource-server/policy/search", urlParamKeys: ["id"], }); updatePolicy = this.makeUpdateRequest({ method: "PUT", path: "/{id}/authz/resource-server/policy/{type}/{policyId}", urlParamKeys: ["id", "type", "policyId"], }); createPolicy = this.makeUpdateRequest({ method: "POST", path: "/{id}/authz/resource-server/policy/{type}", urlParamKeys: ["id", "type"], }); findOnePolicy = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/policy/{type}/{policyId}", urlParamKeys: ["id", "type", "policyId"], catchNotFound: true, }); listDependentPolicies = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/policy/{policyId}/dependentPolicies", urlParamKeys: ["id", "policyId"], }); delPolicy = this.makeRequest({ method: "DELETE", path: "{id}/authz/resource-server/policy/{policyId}", urlParamKeys: ["id", "policyId"], }); listPolicyProviders = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/policy/providers", urlParamKeys: ["id"], }); async createOrUpdatePolicy(payload) { const policyFound = await this.findPolicyByName({ id: payload.id, name: payload.policyName, }); if (policyFound) { await this.updatePolicy({ id: payload.id, policyId: policyFound.id, type: payload.policy.type, }, payload.policy); return this.findPolicyByName({ id: payload.id, name: payload.policyName, }); } else { return this.createPolicy({ id: payload.id, type: payload.policy.type }, payload.policy); } } /** * Scopes */ listAllScopes = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/scope", urlParamKeys: ["id"], }); listAllResourcesByScope = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/scope/{scopeId}/resources", urlParamKeys: ["id", "scopeId"], }); listAllPermissionsByScope = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/scope/{scopeId}/permissions", urlParamKeys: ["id", "scopeId"], }); listPermissionsByResource = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/resource/{resourceId}/permissions", urlParamKeys: ["id", "resourceId"], }); listScopesByResource = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/resource/{resourceName}/scopes", urlParamKeys: ["id", "resourceName"], }); createAuthorizationScope = this.makeUpdateRequest({ method: "POST", path: "{id}/authz/resource-server/scope", urlParamKeys: ["id"], }); updateAuthorizationScope = this.makeUpdateRequest({ method: "PUT", path: "/{id}/authz/resource-server/scope/{scopeId}", urlParamKeys: ["id", "scopeId"], }); getAuthorizationScope = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/scope/{scopeId}", urlParamKeys: ["id", "scopeId"], }); delAuthorizationScope = this.makeRequest({ method: "DELETE", path: "/{id}/authz/resource-server/scope/{scopeId}", urlParamKeys: ["id", "scopeId"], }); /** * Permissions */ findPermissions = this.makeRequest({ method: "GET", path: "{id}/authz/resource-server/permission", urlParamKeys: ["id"], }); createPermission = this.makeUpdateRequest({ method: "POST", path: "/{id}/authz/resource-server/permission/{type}", urlParamKeys: ["id", "type"], }); updatePermission = this.makeUpdateRequest({ method: "PUT", path: "/{id}/authz/resource-server/permission/{type}/{permissionId}", urlParamKeys: ["id", "type", "permissionId"], }); delPermission = this.makeRequest({ method: "DELETE", path: "/{id}/authz/resource-server/permission/{type}/{permissionId}", urlParamKeys: ["id", "type", "permissionId"], }); findOnePermission = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/permission/{type}/{permissionId}", urlParamKeys: ["id", "type", "permissionId"], }); getAssociatedScopes = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/policy/{permissionId}/scopes", urlParamKeys: ["id", "permissionId"], }); getAssociatedResources = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/policy/{permissionId}/resources", urlParamKeys: ["id", "permissionId"], }); getAssociatedPolicies = this.makeRequest({ method: "GET", path: "/{id}/authz/resource-server/policy/{permissionId}/associatedPolicies", urlParamKeys: ["id", "permissionId"], }); getOfflineSessionCount = this.makeRequest({ method: "GET", path: "/{id}/offline-session-count", urlParamKeys: ["id"], }); getInstallationProviders = this.makeRequest({ method: "GET", path: "/{id}/installation/providers/{providerId}", urlParamKeys: ["id", "providerId"], }); pushRevocation = this.makeRequest({ method: "POST", path: "/{id}/push-revocation", urlParamKeys: ["id"], }); addClusterNode = this.makeRequest({ method: "POST", path: "/{id}/nodes", urlParamKeys: ["id"], }); deleteClusterNode = this.makeRequest({ method: "DELETE", path: "/{id}/nodes/{node}", urlParamKeys: ["id", "node"], }); testNodesAvailable = this.makeRequest({ method: "GET", path: "/{id}/test-nodes-available", urlParamKeys: ["id"], }); getKeyInfo = this.makeRequest({ method: "GET", path: "/{id}/certificates/{attr}", urlParamKeys: ["id", "attr"], }); generateKey = this.makeRequest({ method: "POST", path: "/{id}/certificates/{attr}/generate", urlParamKeys: ["id", "attr"], }); downloadKey = this.makeUpdateRequest({ method: "POST", path: "/{id}/certificates/{attr}/download", urlParamKeys: ["id", "attr"], headers: { accept: "application/octet-stream", }, }); generateAndDownloadKey = this.makeUpdateRequest({ method: "POST", path: "/{id}/certificates/{attr}/generate-and-download", urlParamKeys: ["id", "attr"], headers: { accept: "application/octet-stream", }, }); uploadKey = this.makeUpdateRequest({ method: "POST", path: "/{id}/certificates/{attr}/upload", urlParamKeys: ["id", "attr"], }); uploadCertificate = this.makeUpdateRequest({ method: "POST", path: "/{id}/certificates/{attr}/upload-certificate", urlParamKeys: ["id", "attr"], }); updateFineGrainPermission = this.makeUpdateRequest({ method: "PUT", path: "/{id}/management/permissions", urlParamKeys: ["id"], }); listFineGrainPermissions = this.makeRequest({ method: "GET", path: "/{id}/management/permissions", urlParamKeys: ["id"], }); constructor(client) { super(client, { path: "/admin/realms/{realm}/clients", getUrlParams: () => ({ realm: client.realmName, }), getBaseUrl: () => client.baseUrl, }); } /** * Find single protocol mapper by name. */ async findProtocolMapperByName(payload) { const allProtocolMappers = await this.listProtocolMappers({ id: payload.id, ...(payload.realm ? { realm: payload.realm } : {}), }); return allProtocolMappers.find((mapper) => mapper.name === payload.name); } } class ClientScopes extends Resource { find = this.makeRequest({ method: "GET", path: "/client-scopes", }); create = this.makeRequest({ method: "POST", path: "/client-scopes", returnResourceIdInLocationHeader: { field: "id" }, }); /** * Client-Scopes by id */ findOne = this.makeRequest({ method: "GET", path: "/client-scopes/{id}", urlParamKeys: ["id"], catchNotFound: true, }); update = this.makeUpdateRequest({ method: "PUT", path: "/client-scopes/{id}", urlParamKeys: ["id"], }); del = this.makeRequest({ method: "DELETE", path: "/client-scopes/{id}", urlParamKeys: ["id"], }); /** * Default Client-Scopes */ listDefaultClientScopes = this.makeRequest({ method: "GET", path: "/default-default-client-scopes", }); addDefaultClientScope = this.makeRequest({ method: "PUT", path: "/default-default-client-scopes/{id}", urlParamKeys: ["id"], }); delDefaultClientScope = this.makeRequest({ method: "DELETE", path: "/default-default-client-scopes/{id}", urlParamKeys: ["id"], }); /** * Default Optional Client-Scopes */ listDefaultOptionalClientScopes = this.makeRequest({ method: "GET", path: "/default-optional-client-scopes", }); addDefaultOptionalClientScope = this.makeRequest({ method: "PUT", path: "/default-optional-client-scopes/{id}", urlParamKeys: ["id"], }); delDefaultOptionalClientScope = this.makeRequest({ method: "DELETE", path: "/default-optional-client-scopes/{id}", urlParamKeys: ["id"], }); /** * Protocol Mappers */ addMultipleProtocolMappers = this.makeUpdateRequest({ method: "POST", path: "/client-scopes/{id}/protocol-mappers/add-models", urlParamKeys: ["id"], }); addProtocolMapper = this.makeUpdateRequest({ method: "POST", path: "/client-scopes/{id}/protocol-mappers/models", urlParamKeys: ["id"], }); listProtocolMappers = this.makeRequest({ method: "GET", path: "/client-scopes/{id}/protocol-mappers/models", urlParamKeys: ["id"], }); findProtocolMapper = this.makeRequest({ method: "GET", path: "/client-scopes/{id}/protocol-mappers/models/{mapperId}", urlParamKeys: ["id", "mapperId"], catchNotFound: true, }); findProtocolMappersByProtocol = this.makeRequest({ method: "GET", path: "/client-scopes/{id}/protocol-mappers/protocol/{protocol}", urlParamKeys: ["id", "protocol"], catchNotFound: true, }); updateProtocolMapper = this.makeUpdateRequest({ method: "PUT", path: "/client-scopes/{id}/protocol-mappers/models/{mapperId}", urlParamKeys: ["id", "mapperId"], }); delProtocolMapper = this.makeRequest({ method: "DELETE", path: "/client-scopes/{id}/protocol-mappers/models/{mapperId}", urlParamKeys: ["id", "mapperId"], }); /** * Scope Mappings */ listScopeMappings = this.makeRequest({ method: "GET", path: "/client-scopes/{id}/scope-mappings", urlParamKeys: ["id"], }); addClientScopeMappings = this.makeUpdateRequest({ method: "POST", path: "/client-scopes/{id}/scope-mappings/clients/{client}", urlParamKeys: ["id", "client"], }); listClientScopeMappings = this.makeRequest({ method: "GET", path: "/client-scopes/{id}/scope-mappings/clients/{client}", urlParamKeys: ["id", "client"], }); listAvailableClientScopeMappings = this.makeRequest({ method: "GET", path: "/client-scopes/{id}/scope-mappings/clients/{client}/available", urlParamKeys: ["id", "client"], }); listCompositeClientScopeMappings = this.makeRequest({ method: "GET", path: "/client-scopes/{id}/scope-mappings/clients/{client}/composite", urlParamKeys: ["id", "client"], }); delClientScopeMappings = this.makeUpdateRequest({ method: "DELETE", path: "/client-scopes/{id}/scope-mappings/clients/{client}", urlParamKeys: ["id", "client"], }); addRealmScopeMappings = this.makeUpdateRequest({ method: "POST", path: "/client-scopes/{id}/scope-mappings/realm", urlParamKeys: ["id"], }); listRealmScopeMappings = this.makeRequest({ method: "GET", path: "/client-scopes/{id}/scope-mappings/realm", urlParamKeys: ["id"], }); listAvailableRealmScopeMappings = this.makeRequest({ method: "GET", path: "/client-scopes/{id}/scope-mappings/realm/available", urlParamKeys: ["id"], }); listCompositeRealmScopeMappings = this.makeRequest({ method: "GET", path: "/client-scopes/{id}/scope-mappings/realm/composite", urlParamKeys: ["id"], }); delRealmScopeMappings = this.makeUpdateRequest({ method: "DELETE", path: "/client-scopes/{id}/scope-mappings/realm", urlParamKeys: ["id"], }); constructor(client) { super(client, { path: "/admin/realms/{realm}", getUrlParams: () => ({ realm: client.realmName, }), getBaseUrl: () => client.baseUrl, }); } /** * Find client scope by name. */ async findOneByName(payload) { const allScopes = await this.find({ ...(payload.realm ? { realm: payload.realm } : {}), }); return allScopes.find((item) => item.name === payload.name); } /** * Delete client scope by name. */ async delByName(payload) { const scope = await this.findOneByName(payload); if (!scope) { throw new Error("Scope not found."); } await this.del({ ...(payload.realm ? { realm: payload.realm } : {}), id: scope.id, }); } /** * Find single protocol mapper by name. */ async findProtocolMapperByName(payload) { const allProtocolMappers = await this.listProtocolMappers({ id: payload.id, ...(payload.realm ? { realm: payload.realm } : {}), }); return allProtocolMappers.find((mapper) => mapper.name === payload.name); } } class Components extends Resource { /** * components * https://www.keycloak.org/docs-api/11.0/rest-api/#_component_resource */ find = this.makeRequest({ method: "GET", }); create = this.makeRequest({ method: "POST", returnResourceIdInLocationHeader: { field: "id" }, }); findOne = this.makeRequest({ method: "GET", path: "/{id}", urlParamKeys: ["id"], catchNotFound: true, }); update = this.makeUpdateRequest({ method: "PUT", path: "/{id}", urlParamKeys: ["id"], }); del = this.makeRequest({ method: "DELETE", path: "/{id}", urlParamKeys: ["id"], }); listSubComponents = this.makeRequest({ method: "GET", path: "/{id}/sub-component-types", urlParamKeys: ["id"], queryParamKeys: ["type"], }); constructor(client) { super(client, { path: "/admin/realms/{realm}/components", getUrlParams: () => ({ realm: client.realmName, }), getBaseUrl: () => client.baseUrl, }); } } class Groups extends Resource { find = this.makeRequest({ method: "GET", queryParamKeys: [ "search", "q", "exact", "briefRepresentation", "first", "max", ], }); create = this.makeRequest({ method: "POST", returnResourceIdInLocationHeader: { field: "id" }, }); updateRoot = this.makeRequest({ method: "P