Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(shared): Stop using jquery (for ajax and selection) (closes #76) #1114

Merged
merged 1 commit into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions packages/hawtio/src/core/config-manager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import $ from 'jquery'
import { Plugin } from './core'
import { Logger } from './logging'

Expand Down Expand Up @@ -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<boolean> {
Expand Down
7 changes: 3 additions & 4 deletions packages/hawtio/src/core/core.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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
}
Expand Down
56 changes: 0 additions & 56 deletions packages/hawtio/src/plugins/auth/keycloak/keycloak-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -139,7 +138,6 @@ class KeycloakService implements IKeycloakService {
userService.setToken(userProfile.token)
}

this.setupJQueryAjax()
this.setupFetch()

return true
Expand All @@ -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
Expand Down
45 changes: 0 additions & 45 deletions packages/hawtio/src/plugins/auth/oidc/oidc-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -285,7 +284,6 @@ export class OidcService implements IOidcService {
// clear the URL bar
window.history.replaceState(null, '', loginData.h)

this.setupJQueryAjax()
this.setupFetch()

return {
Expand Down Expand Up @@ -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) {
Expand Down
46 changes: 24 additions & 22 deletions packages/hawtio/src/plugins/shared/jolokia-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -258,35 +257,38 @@ class JolokiaService implements IJolokiaService {
// browser popup)
// TOCHECK: scenarios when there's no server side session (Keycloak, OIDC)
return new Promise<string>((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
})
})
}
Expand Down
2 changes: 0 additions & 2 deletions packages/hawtio/src/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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 })
Expand Down
Loading