Skip to content

Commit

Permalink
fix: Upgrade of hawtio/react to 1.5.0
Browse files Browse the repository at this point in the history
* With this upgrade, the transport method of jolokia requests has been
  changed from jquery to fetch. This meant a number of changes in the
  development server in order to continue working.

* management-service.ts
 * Rather than just blindly connect to a target app, try testing the
   connection first and if it fails do not change location / open a new
   browser tab but notify the user with the event service

* oauth/[....]service.ts
 * Mandate a Content-Type of application/json when using fetch. Due to CORS
   this is not possible when actually calling fetch(uri, { headers })
 * Ensure when regsitering the fetch interceptor that the headers that
   have been created are not overwritten by any headers already contained
   in the requestConfig.

* webpack.config.dev.js
 * Re-implements management endpoint to conduct a sub-request using fetch
   to the master endpoint rather than redirecting. The fetch function appears
   to not redirect correctly when a Content-Length header is specified
 * Implementing a sub-request is the same way that the gateway component
   solves this issue
 * Ensures the token is passed to the sub-request
 * Logs whether the request body has been incorrectly parsed empty by
   the Express server in the webpack-dev-server.
  • Loading branch information
phantomjinx committed Oct 14, 2024
1 parent 44cd63c commit e8c1a42
Show file tree
Hide file tree
Showing 12 changed files with 271 additions and 178 deletions.
2 changes: 1 addition & 1 deletion packages/kubernetes-api-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
"dependencies": {
"@hawtio/online-kubernetes-api": "workspace:*",
"@hawtio/react": "^1.3.0",
"@hawtio/react": "^1.5.0",
"@patternfly/react-core": "^5.3.3",
"@patternfly/react-styles": "^5.3.1",
"@patternfly/react-table": "^5.3.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/kubernetes-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
},
"dependencies": {
"@hawtio/online-oauth": "workspace:*",
"@hawtio/react": "^1.3.0",
"@hawtio/react": "^1.5.0",
"@types/jquery": "^3.5.30",
"@types/jsonpath": "^0.2.4",
"@types/node": "^20.14.9",
Expand Down
2 changes: 1 addition & 1 deletion packages/management-api-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
"dependencies": {
"@hawtio/online-management-api": "workspace:*",
"@hawtio/react": "^1.3.0",
"@hawtio/react": "^1.5.0",
"@patternfly/react-core": "^5.3.3",
"@patternfly/react-styles": "^5.3.1",
"@patternfly/react-table": "^5.3.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/management-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
},
"dependencies": {
"@hawtio/online-kubernetes-api": "workspace:*",
"@hawtio/react": "^1.3.0",
"@hawtio/react": "^1.5.0",
"eventemitter3": "^5.0.1",
"jolokia.js": "^2.1.7",
"jquery": "^3.7.0",
Expand Down
29 changes: 27 additions & 2 deletions packages/management-api/src/management-service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EventEmitter } from 'eventemitter3'
import { ManagedPod } from './managed-pod'
import { Connection, Connections, connectService } from '@hawtio/react'
import { Connection, ConnectionTestResult, Connections, connectService, eventService } from '@hawtio/react'
import {
k8Service,
k8Api,
Expand Down Expand Up @@ -389,7 +389,32 @@ export class ManagementService extends EventEmitter implements Paging {
return
}

connectService.connect(connection)
connectService
.testConnection(connection)
.then((result: ConnectionTestResult) => {
if (result.status !== 'reachable') {
const msg = `There was a problem connecting to the jolokia service ${connectName}`
log.error(msg)
eventService.notify({ type: 'danger', message: msg })
return
}

if (result.message.includes('auth failed')) {
const msg = `A problem occurred with authentication while trying to connect to the jolokia service ${connectName}`
log.error(msg)
eventService.notify({ type: 'danger', message: msg })
return
}

connectService.connect(connection)
})
.catch(error => {
const msg = `A problem occurred while trying to connect to the jolokia service ${connectName}`
log.error(msg)
log.error(error)
eventService.notify({ type: 'danger', message: msg })
return
})
}

/********************
Expand Down
2 changes: 1 addition & 1 deletion packages/oauth-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
"dependencies": {
"@hawtio/online-oauth": "workspace:*",
"@hawtio/react": "^1.3.0",
"@hawtio/react": "^1.5.0",
"@patternfly/react-core": "^5.3.1",
"@patternfly/react-styles": "^5.3.0",
"@patternfly/react-table": "^5.3.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/oauth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"prepack": "yarn build && yarn replace-version"
},
"dependencies": {
"@hawtio/react": "^1.3.0",
"@hawtio/react": "^1.5.0",
"@thumbmarkjs/thumbmarkjs": "^0.14.8",
"babel-jest": "^29.6.1",
"fetch-intercept": "^2.4.0",
Expand Down
15 changes: 13 additions & 2 deletions packages/oauth/src/form/form-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type LoginOptions = {

interface Headers {
Authorization: string
'Content-Type': string
'X-XSRF-TOKEN'?: string
}

Expand Down Expand Up @@ -122,11 +123,13 @@ export class FormService implements OAuthProtoService {

log.debug('Intercept Fetch API to attach auth token to authorization header')
this.fetchUnregister = fetchIntercept.register({
request: (url, config) => {
request: (url, requestConfig) => {
log.debug('Fetch intercepted for oAuth authentication')

// Include any requestConfig headers to ensure they are retained
let headers: Headers = {
Authorization: `Bearer ${this.userProfile.getToken()}`,
'Content-Type': 'application/json',
}

// For CSRF protection with Spring Security
Expand All @@ -139,7 +142,15 @@ export class FormService implements OAuthProtoService {
}
}

return [url, { headers, ...config }]
/*
* if requestConfig exists and already has a set of headers
*/
if (requestConfig && requestConfig.headers) {
headers = { ...requestConfig.headers, ...headers }
}

// headers must be 2nd so that it overwrites headers property in requestConfig
return [url, { ...requestConfig, headers }]
},
})
}
Expand Down
13 changes: 12 additions & 1 deletion packages/oauth/src/openshift/osoauth-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ interface UserObject {

interface Headers {
Authorization: string
'Content-Type': string
'X-XSRF-TOKEN'?: string
}

Expand Down Expand Up @@ -120,8 +121,10 @@ export class OSOAuthService implements OAuthProtoService {
this.doLogout(config)
}

// Include any requestConfig headers to ensure they are retained
let headers: Headers = {
Authorization: `Bearer ${this.userProfile.getToken()}`,
'Content-Type': 'application/json',
}

// For CSRF protection with Spring Security
Expand All @@ -134,7 +137,15 @@ export class OSOAuthService implements OAuthProtoService {
}
}

return [url, { headers, ...requestConfig }]
/*
* if requestConfig exists and already has a set of headers
*/
if (requestConfig && requestConfig.headers) {
headers = { ...requestConfig.headers, ...headers }
}

// headers must be 2nd so that it overwrites headers property in requestConfig
return [url, { ...requestConfig, headers }]
},
})
}
Expand Down
2 changes: 1 addition & 1 deletion packages/online-shell/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"@hawtio/online-kubernetes-api": "workspace:*",
"@hawtio/online-management-api": "workspace:*",
"@hawtio/online-oauth": "workspace:*",
"@hawtio/react": "^1.3.0",
"@hawtio/react": "^1.5.0",
"@patternfly/react-core": "^5.3.3",
"@patternfly/react-styles": "^5.3.1",
"@types/node": "^20.14.9",
Expand Down
79 changes: 74 additions & 5 deletions packages/online-shell/webpack.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const historyApiFallback = require('connect-history-api-fallback')
const path = require('path')
const url = require('url')
const dotenv = require('dotenv')
const express = require('express')
const { common } = require('./webpack.config.common.js')

// this will update the process.env with environment variables in .env file
Expand Down Expand Up @@ -121,6 +122,15 @@ module.exports = (env, argv) => {
throw new Error('webpack-dev-server is not defined')
}

/*
* Ensure that dev server properly handles json in request body
* Important to keep a high limit as the default of 100kb can be
* exceeded by request bodies resulting in the parser transmitting
* an empty body.
*/
devServer.app.use(express.json({ type: '*/json', limit: '50mb', strict: false }))
devServer.app.use(express.urlencoded({ extended: false }))

// Redirect / or /${publicPath} to /${publicPath}/
devServer.app.get('/', (_, res) => res.redirect(`${publicPath}/`))
devServer.app.get(`/${publicPath}$`, (_, res) => res.redirect(`${publicPath}/`))
Expand Down Expand Up @@ -193,16 +203,75 @@ module.exports = (env, argv) => {
}

/* Redirects from management alias path to full master path */
const management = (req, res, next) => {
const management = async (req, res, next) => {
const url = /\/management\/namespaces\/(.+)\/pods\/(http|https):([^/]+)\/(.+)/
const match = req.originalUrl.match(url)
const redirectPath = `/master/api/v1/namespaces/${match[1]}/pods/${match[2]}:${match[3]}/proxy/${match[4]}`
if (match) {
// 307 - post redirect
res.redirect(307, redirectPath)
} else {
if (!match) {
next()
}

/*
* Redirect will no longer work since fetch is being used by
* jolokia instead and request contains Content-Length header.
* So perform a sub-request instead on master to return the
* correct response
*/
const origin = `http://localhost:${devPort}`
const uri = `${origin}${redirectPath}`

/*
* Ensure the authorization token is passed to the sub request
*/
const headers = new Headers({
Authorization: req.get('Authorization'),
})

let response
if (req.method === 'GET') {
response = await fetch(uri, {
method: req.method,
headers: headers,
})
} else {
const body = req.body
const isEmptyObject = typeof body === 'object' && Object.keys(body).length === 0
const isEmptyArray = Array.isArray(body) && body.length === 0

let msg
if (!body) {
msg = `Error (dev-server): undefined body found in POST request ${redirectPath}`
console.warn(msg, body)
} else if (isEmptyArray) {
msg = `Error (dev-server): empty body array found in POST request ${redirectPath}`
console.warn(msg, body)
} else if (isEmptyObject) {
msg = `Error (dev-server): empty body object found in POST request ${redirectPath}`
console.warn(msg, body)
} else {
console.log(`Body in request ${redirectPath} to be passed to master`, body)
}

response = await fetch(uri, {
method: req.method,
body: JSON.stringify(body),
headers: headers,
})
}

if (!response.ok) {
res.status(response.status).send(response.statusText)
} else {
var data
try {
data = await response.json()
} catch (error) {
console.error('Error (dev-server): error response from master: ', error)
data = await response.text()
}

res.status(response.status).send(data)
}
}

const username = 'developer'
Expand Down
Loading

0 comments on commit e8c1a42

Please sign in to comment.