From 69d09cc8e2d9f1246fe4fe5392cefc5c94444b75 Mon Sep 17 00:00:00 2001 From: Grzegorz Grzybek Date: Thu, 12 Sep 2024 08:46:41 +0200 Subject: [PATCH] fix(shared): Stop using jquery (for ajax and selection) (closes #76) --- packages/hawtio/src/core/config-manager.ts | 18 ++++-- packages/hawtio/src/core/core.ts | 7 +-- .../plugins/auth/keycloak/keycloak-service.ts | 56 ------------------- .../src/plugins/auth/oidc/oidc-service.ts | 45 --------------- .../src/plugins/shared/jolokia-service.ts | 46 +++++++-------- packages/hawtio/src/setupTests.ts | 2 - 6 files changed, 39 insertions(+), 135 deletions(-) diff --git a/packages/hawtio/src/core/config-manager.ts b/packages/hawtio/src/core/config-manager.ts index de7fd4d9..1d2393c8 100644 --- a/packages/hawtio/src/core/config-manager.ts +++ b/packages/hawtio/src/core/config-manager.ts @@ -1,4 +1,3 @@ -import $ from 'jquery' import { Plugin } from './core' import { Logger } from './logging' @@ -192,14 +191,21 @@ class ConfigManager { private updateHref(id: string, path: string, moveToLast: boolean = false): void { log.info('Updating href for', id, '-', path, moveToLast) - const elm = $(id) - elm.prop('disabled', true) - elm.attr({ href: path }) + const elm = document.querySelector(id) as HTMLInputElement + if (!elm) { + return + } + if ('disabled' in elm) { + elm.disabled = true + } + elm.setAttribute('href', path) if (moveToLast) { elm.remove() - $('head').append(elm) + document.querySelector('head')?.append(elm) + } + if ('disabled' in elm) { + elm.disabled = false } - elm.prop('disabled', false) } async isRouteEnabled(path: string): Promise { diff --git a/packages/hawtio/src/core/core.ts b/packages/hawtio/src/core/core.ts index 9b8ef528..cf808457 100644 --- a/packages/hawtio/src/core/core.ts +++ b/packages/hawtio/src/core/core.ts @@ -1,6 +1,5 @@ import { userService } from '@hawtiosrc/auth' import { importRemote, ImportRemoteOptions } from '@module-federation/utilities' -import $ from 'jquery' import { configManager } from './config-manager' import { eventService } from './event-service' import { log } from './globals' @@ -162,9 +161,9 @@ class HawtioCore { } private documentBase(): string | undefined { - const base = $('head').find('base') - if (base && base.length > 0) { - return base.attr('href') + const base = document.querySelector('head base') + if (base) { + return base.getAttribute('href') ?? undefined } return undefined } diff --git a/packages/hawtio/src/plugins/auth/keycloak/keycloak-service.ts b/packages/hawtio/src/plugins/auth/keycloak/keycloak-service.ts index 7966ffbb..32204a23 100644 --- a/packages/hawtio/src/plugins/auth/keycloak/keycloak-service.ts +++ b/packages/hawtio/src/plugins/auth/keycloak/keycloak-service.ts @@ -2,7 +2,6 @@ import { userService } from '@hawtiosrc/auth' import { ResolveUser } from '@hawtiosrc/auth/user-service' import { fetchPath } from '@hawtiosrc/util/fetch' import { basicAuthHeaderValue, getCookie } from '@hawtiosrc/util/https' -import $ from 'jquery' import Keycloak, { KeycloakConfig, KeycloakInitOptions, KeycloakPkceMethod, KeycloakProfile } from 'keycloak-js' import { UserProfile } from '../types' import { PATH_KEYCLOAK_CLIENT_CONFIG, PATH_KEYCLOAK_ENABLED, PATH_KEYCLOAK_VALIDATE, log } from './globals' @@ -139,7 +138,6 @@ class KeycloakService implements IKeycloakService { userService.setToken(userProfile.token) } - this.setupJQueryAjax() this.setupFetch() return true @@ -163,60 +161,6 @@ class KeycloakService implements IKeycloakService { userService.addLogoutHook('keycloak', logout) } - private async setupJQueryAjax() { - const keycloak = await this.keycloak - const config = await this.config - if (!keycloak || !config) { - return - } - - log.debug('Set authorization header to Keycloak token for AJAX requests') - const beforeSend = (xhr: JQueryXHR, settings: JQueryAjaxSettings) => { - const logPrefix = 'jQuery -' - if (!keycloak.authenticated || keycloak.isTokenExpired(KEYCLOAK_TOKEN_MINIMUM_VALIDITY)) { - log.debug(logPrefix, 'Try to update token for request:', settings.url) - this.updateToken( - token => { - if (token) { - log.debug(logPrefix, 'Keycloak token refreshed. Set new value to userService') - userService.setToken(token) - } - log.debug(logPrefix, 'Re-sending request after successfully updating Keycloak token:', settings.url) - $.ajax(settings) - }, - () => { - log.debug(logPrefix, 'Logging out due to token update failed') - userService.logout() - }, - ) - return false - } - - if (config.jaas) { - // Use BearerTokenLoginModule on server side - if (keycloak.profile && keycloak.profile.username && keycloak.token) { - const headerValue = basicAuthHeaderValue(keycloak.profile.username, keycloak.token) - xhr.setRequestHeader('Authorization', headerValue) - } else { - log.error(logPrefix, 'Keycloak username or token not found in JAAS mode:', keycloak.profile, keycloak.token) - } - } else { - // Otherwise, bearer token is used - xhr.setRequestHeader('Authorization', `Bearer ${keycloak.token}`) - } - - // For CSRF protection with Spring Security - const token = getCookie('XSRF-TOKEN') - if (token) { - log.debug(logPrefix, 'Set XSRF token header from cookies') - xhr.setRequestHeader('X-XSRF-TOKEN', token) - } - - return // To suppress ts(7030) - } - $.ajaxSetup({ beforeSend }) - } - private async setupFetch() { const keycloak = await this.keycloak const config = await this.config diff --git a/packages/hawtio/src/plugins/auth/oidc/oidc-service.ts b/packages/hawtio/src/plugins/auth/oidc/oidc-service.ts index f2dff7d9..608ff8ab 100644 --- a/packages/hawtio/src/plugins/auth/oidc/oidc-service.ts +++ b/packages/hawtio/src/plugins/auth/oidc/oidc-service.ts @@ -4,7 +4,6 @@ import { jwtDecode } from 'jwt-decode' import * as oidc from 'oauth4webapi' import { AuthorizationServer, Client, OAuth2Error } from 'oauth4webapi' import { fetchPath } from '@hawtiosrc/util/fetch' -import $ from 'jquery' import { getCookie } from '@hawtiosrc/util/https' const pluginName = 'hawtio-oidc' @@ -285,7 +284,6 @@ export class OidcService implements IOidcService { // clear the URL bar window.history.replaceState(null, '', loginData.h) - this.setupJQueryAjax() this.setupFetch() return { @@ -385,49 +383,6 @@ export class OidcService implements IOidcService { } } - private async setupJQueryAjax() { - let userInfo = await this.userInfo - if (!userInfo) { - return - } - - log.debug('Set authorization header to OIDC token for AJAX requests') - const beforeSend = (xhr: JQueryXHR, settings: JQueryAjaxSettings) => { - const logPrefix = 'jQuery -' - if (userInfo && (!userInfo.access_token || this.isTokenExpiring(userInfo.at_exp))) { - log.debug(logPrefix, 'Try to update token for request:', settings.url) - this.updateToken( - _userInfo => { - if (_userInfo) { - userInfo = _userInfo - log.debug(logPrefix, 'OIDC token refreshed. Set new value to userService') - userService.setToken(userInfo.access_token!) - } - log.debug(logPrefix, 'Re-sending request after successfully updating OIDC token:', settings.url) - $.ajax(settings) - }, - () => { - log.debug(logPrefix, 'Logging out due to token update failed') - userService.logout() - }, - ) - return false - } - - xhr.setRequestHeader('Authorization', `Bearer ${userInfo!.access_token}`) - - // For CSRF protection with Spring Security - const token = getCookie('XSRF-TOKEN') - if (token) { - log.debug(logPrefix, 'Set XSRF token header from cookies') - xhr.setRequestHeader('X-XSRF-TOKEN', token) - } - - return // To suppress ts(7030) - } - $.ajaxSetup({ beforeSend }) - } - private async setupFetch() { let userInfo = await this.userInfo if (!userInfo) { diff --git a/packages/hawtio/src/plugins/shared/jolokia-service.ts b/packages/hawtio/src/plugins/shared/jolokia-service.ts index 5a5c9474..ce6e1cf0 100644 --- a/packages/hawtio/src/plugins/shared/jolokia-service.ts +++ b/packages/hawtio/src/plugins/shared/jolokia-service.ts @@ -33,7 +33,6 @@ import { WriteResponseValue, } from 'jolokia.js' import Jolokia, { IJolokiaSimple, SimpleRequestOptions, SimpleResponseCallback } from '@jolokia.js/simple' -import $ from 'jquery' import { is, object } from 'superstruct' import { PARAM_KEY_CONNECTION, @@ -258,35 +257,38 @@ class JolokiaService implements IJolokiaService { // browser popup) // TOCHECK: scenarios when there's no server side session (Keycloak, OIDC) return new Promise((resolve, reject) => { - $.ajax(path) - .done((data: string, _textStatus: string, xhr: JQueryXHR) => { - if (xhr.status !== 200) { - reject() - return - } - - try { - const resp = typeof data === 'string' ? JSON.parse(data) : data - if ('value' in resp && 'agent' in resp.value) { - log.debug('Found jolokia agent at:', path, ', version:', resp.value.agent) - resolve(path) + fetch(path) + .then(async (response: Response) => { + // for fetch(), .then() is fed with any response code, while .catch() is used to handle + // more serious issues (like connection refused) + if (response.status == 200) { + try { + const resp = await response.json() + if ('value' in resp && 'agent' in resp.value) { + log.debug('Found jolokia agent at:', path, ', version:', resp.value.agent) + resolve(path) + return + } + } catch (e) { + // Parse error should mean redirect to html + reject(e) return } - } catch (e) { - // Parse error should mean redirect to html - reject(e) + reject() return } - reject() - }) - .fail((xhr: JQueryXHR) => { - if (xhr.status === 401 || xhr.status === 403) { + if (response.status === 401 || response.status === 403) { // I guess this could be it... - log.debug('Using URL:', path, 'assuming it could be an agent but got return code:', xhr.status) + log.debug('Using URL:', path, 'assuming it could be an agent but got return code:', response.status) resolve(path) return } - reject(`${xhr.status} ${xhr.statusText}`) + reject() + return + }) + .catch(error => { + reject(error) + return }) }) } diff --git a/packages/hawtio/src/setupTests.ts b/packages/hawtio/src/setupTests.ts index 8202b6a4..5f6d5124 100644 --- a/packages/hawtio/src/setupTests.ts +++ b/packages/hawtio/src/setupTests.ts @@ -5,7 +5,6 @@ import '@testing-library/jest-dom' import crypto from 'crypto' import fetchMock from 'jest-fetch-mock' -import $ from 'jquery' import { TextDecoder, TextEncoder } from 'util' fetchMock.enableMocks() @@ -27,7 +26,6 @@ fetchMock.mockResponse(req => { // To fix "jQuery is not defined" error // eslint-disable-next-line @typescript-eslint/no-explicit-any declare const global: any -global.$ = global.jQuery = $ // For testing crypto Object.defineProperty(global, 'crypto', { value: crypto.webcrypto })