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

feat(connect): Handle improved session management for proxy connectio… #948

Merged
merged 1 commit into from
May 20, 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
9 changes: 9 additions & 0 deletions packages/hawtio/src/plugins/connect/ConnectionStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@ import { PluggedIcon, UnpluggedIcon } from '@patternfly/react-icons'
*/
export const ConnectionStatus: React.FunctionComponent = () => {
const [reachable, setReachable] = useState<ConnectStatus>('not-reachable')
const [username, setUsername] = useState('')

const connectionId = connectService.getCurrentConnectionId()
const connectionName = connectService.getCurrentConnectionName()

useEffect(() => {
connectService.getCurrentCredentials().then(credentials => {
const username = credentials ? credentials.username : ''
setUsername(username)
})
}, [])

useEffect(() => {
const check = async () => {
const connection = await connectService.getCurrentConnection()
Expand Down Expand Up @@ -41,6 +49,7 @@ export const ConnectionStatus: React.FunctionComponent = () => {
<>
{icon}
{connectionName ? connectionName : ''}
{username ? ' (' + username + ')' : ''}
</>
)
}
10 changes: 2 additions & 8 deletions packages/hawtio/src/plugins/connect/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,8 @@ function isConnectLogin(): boolean {
export function registerUserHooks() {
const credPromise = connectService.getCurrentCredentials()

userService.addFetchUserHook('connect', async resolve => {
const credentials = await credPromise
if (!credentials) {
return false
}
resolve({ username: credentials.username, isLogin: true })
return true
})
// no fetchUserHook - remote Jolokia credentials are not Hawtio credentials

userService.addLogoutHook('connect', async () => {
const credentials = await credPromise
if (!credentials) {
Expand Down
4 changes: 4 additions & 0 deletions packages/hawtio/src/plugins/connect/login/ConnectLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ export const ConnectLogin: React.FunctionComponent = () => {
setLoginFailed(true)
setLoginFailedMessage('Incorrect username or password')
break
case 'session-expired':
setLoginFailed(true)
setLoginFailedMessage('Session expired. Re-login in main window.')
break
case 'throttled': {
const { retryAfter } = result
setLoginFailed(true)
Expand Down
20 changes: 17 additions & 3 deletions packages/hawtio/src/plugins/shared/connect-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ export type ConnectionCredentials = {
password: string
}

export type LoginResult = { type: 'success' } | { type: 'failure' } | { type: 'throttled'; retryAfter: number }
export type LoginResult =
| { type: 'success' }
| { type: 'failure' }
| { type: 'throttled'; retryAfter: number }
| { type: 'session-expired' }

/**
* Remote connection status. "not-reachable-securely" is for connections that can't be used in insecure contexts.
Expand Down Expand Up @@ -251,15 +255,21 @@ class ConnectService implements IConnectService {
log.debug('Testing connection:', toString(connection))
// test the connection without credentials, so 401 or 403 are treated as "reachable", but actual
// connection will require authentication
// we can't prevent showing native popup dialog with xhr, but fetch + "credentials:'omit'" works
// When server returns "WWW-Authenticate: Basic xxx", we can't prevent showing native popup dialog with xhr,
// but we can do that with fetch + "credentials:'omit'"
// However we have to include the credentials, because it's not only about "Authorization" header (which may
// be created using data stored in browser's password manager) - it's also about cookies. When testing
// reachability of remote Jolokia agent, we have to send JSESSIONID, because /proxy/* is protected. However
// we prevent native browser dialog because Hawtio proxy translates "WWW-Authenticate: Basic xxx", so it
// doesn't include "Basic" scheme. This is enough for the browser to skip the dialog. Even with xhr.
return new Promise<ConnectionTestResult>((resolve, reject) => {
try {
fetch(this.getJolokiaUrl(connection), {
method: 'post',
// with application/json, I'm getting "CanceledError: Request stream has been aborted" when running
// via hawtioMiddleware...
headers: { 'Content-Type': 'text/json' },
credentials: 'omit',
credentials: 'same-origin',
body: JSON.stringify({ type: 'version' }),
})
.then(response => {
Expand Down Expand Up @@ -351,6 +361,10 @@ class ConnectService implements IConnectService {
resolve({ type: 'throttled', retryAfter })
return
}
if (xhr.status === 403 && 'SESSION_EXPIRED' === xhr.getResponseHeader('Hawtio-Forbidden-Reason')) {
resolve({ type: 'session-expired' })
return
}
resolve({ type: 'failure' })
},
},
Expand Down
Loading