mirror of
https://github.com/OpenNebula/one.git
synced 2025-01-18 06:03:39 +03:00
F OpenNebula/one#6599: External VNC management (#3304)
Co-authored-by: Tino Vázquez <cvazquez@opennebula.io>
This commit is contained in:
parent
fa73142e7d
commit
13b6f4aaa4
@ -55,7 +55,7 @@ const showSupportTab = (routes = [], find = true) => {
|
||||
const SunstoneApp = () => {
|
||||
const [getSupport, { isSuccess }] = useLazyCheckOfficialSupportQuery()
|
||||
const { changeAppTitle } = useGeneralApi()
|
||||
const { isLogged } = useAuth()
|
||||
const { isLogged, externalRedirect } = useAuth()
|
||||
const { views, view } = useViews()
|
||||
|
||||
useEffect(() => {
|
||||
@ -95,7 +95,10 @@ const SunstoneApp = () => {
|
||||
<NotifierUpload />
|
||||
</>
|
||||
)}
|
||||
<Router redirectWhenAuth={PATH.DASHBOARD} endpoints={endpoints} />
|
||||
<Router
|
||||
redirectWhenAuth={externalRedirect || PATH.DASHBOARD}
|
||||
endpoints={endpoints}
|
||||
/>
|
||||
</AuthLayout>
|
||||
)
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ import { Translate } from 'client/components/HOC'
|
||||
import { GuacamoleSession, T } from 'client/constants'
|
||||
import { downloadFile } from 'client/utils'
|
||||
|
||||
import { useLazyGetGuacamoleSessionQuery } from 'client/features/OneApi/vm'
|
||||
import { useLazyGetGuacamoleSessionFileQuery } from 'client/features/OneApi/vm'
|
||||
|
||||
const useStyles = makeStyles(({ palette }) => ({
|
||||
customPopper: {
|
||||
@ -376,22 +376,20 @@ const GuacamoleDownloadConButton = memo(
|
||||
* @returns {ReactElement} Button to make screenshot form current session
|
||||
*/
|
||||
(session) => {
|
||||
const [getSession] = useLazyGetGuacamoleSessionQuery()
|
||||
|
||||
const { id, vmID, client } = session
|
||||
|
||||
const [getSession] = useLazyGetGuacamoleSessionFileQuery()
|
||||
const { id, vmID, client, typeConnection } = session
|
||||
const handleClick = useCallback(async () => {
|
||||
if (!client) return
|
||||
|
||||
const res = await getSession({
|
||||
id: vmID,
|
||||
type: 'rdp',
|
||||
type: typeConnection,
|
||||
download: true,
|
||||
}).unwrap()
|
||||
|
||||
if (res) {
|
||||
const blob = new Blob([atob(res)], { type: 'text/plain' })
|
||||
downloadFile(new File([blob], 'connection.txt'))
|
||||
downloadFile(new File([blob], `${id}.${typeConnection}`))
|
||||
}
|
||||
}, [client])
|
||||
|
||||
@ -402,6 +400,14 @@ const GuacamoleDownloadConButton = memo(
|
||||
title={
|
||||
<Typography variant="subtitle2">
|
||||
<Translate word={T.DownloadConecctionFile} />
|
||||
<br />
|
||||
<Translate
|
||||
word={
|
||||
typeConnection === 'rdp'
|
||||
? T.DownloadConnectionRDP
|
||||
: T.DownloadConnectionVNC
|
||||
}
|
||||
/>
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
|
@ -13,9 +13,10 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { Redirect, Route } from 'react-router-dom'
|
||||
import { Redirect, Route, useLocation } from 'react-router-dom'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useAuth, useAuthApi } from 'client/features/Auth'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
/**
|
||||
* Private route.
|
||||
@ -26,6 +27,12 @@ import { useAuth } from 'client/features/Auth'
|
||||
*/
|
||||
const ProtectedRoute = (props) => {
|
||||
const { isLogged: isAuthenticated } = useAuth()
|
||||
const { changeExternalRedirect } = useAuthApi()
|
||||
const { pathname, search } = useLocation()
|
||||
|
||||
useEffect(() => {
|
||||
!isAuthenticated && changeExternalRedirect(`${pathname}${search}`)
|
||||
}, [])
|
||||
|
||||
return isAuthenticated ? <Route {...props} /> : <Redirect to="/" />
|
||||
}
|
||||
|
@ -1875,7 +1875,9 @@ module.exports = {
|
||||
ImportIntoDatastore: 'Import into Datastore',
|
||||
DownloadAppToOpenNebula: 'Download App to OpenNebula',
|
||||
DownloadApp: 'Download App',
|
||||
DownloadConecctionFile: 'Download connection File',
|
||||
DownloadConecctionFile: 'Download connection file.',
|
||||
DownloadConnectionRDP: 'This file is for the Microsoft remote desktop app',
|
||||
DownloadConnectionVNC: 'This file is for the TigerVNC Viewer app',
|
||||
DownloadDefaultImage: 'Download Default Image',
|
||||
ExportAppNameConcept:
|
||||
'Name that the resource will get for description purposes',
|
||||
|
@ -184,8 +184,11 @@ const Guacamole = () => {
|
||||
<GuacamoleReconnectReadOnlyButton {...session} />
|
||||
)}
|
||||
{type === VM_ACTIONS.SSH && <GuacamoleSSHParams {...session} />}
|
||||
{type === VM_ACTIONS.RDP && (
|
||||
<GuacamoleDownloadConButton {...session} />
|
||||
{[VM_ACTIONS.VNC, VM_ACTIONS.RDP].includes(type) && (
|
||||
<GuacamoleDownloadConButton
|
||||
{...session}
|
||||
typeConnection={type}
|
||||
/>
|
||||
)}
|
||||
<GuacamoleReconnectButton {...session} />
|
||||
<GuacamoleScreenshotButton {...session} />
|
||||
|
@ -15,21 +15,21 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useDispatch, useSelector, shallowEqual } from 'react-redux'
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux'
|
||||
|
||||
import { ResourceView } from 'client/apps/sunstone/routes'
|
||||
import {
|
||||
DEFAULT_LANGUAGE,
|
||||
DEFAULT_SCHEME,
|
||||
ONEADMIN_ID,
|
||||
RESOURCE_NAMES,
|
||||
_APPS,
|
||||
} from 'client/constants'
|
||||
import { actions, name as authSlice, logout } from 'client/features/Auth/slice'
|
||||
import { name as generalSlice } from 'client/features/General/slice'
|
||||
import { name as authSlice, actions, logout } from 'client/features/Auth/slice'
|
||||
import groupApi from 'client/features/OneApi/group'
|
||||
import systemApi from 'client/features/OneApi/system'
|
||||
import { ResourceView } from 'client/apps/sunstone/routes'
|
||||
import { areStringEqual } from 'client/models/Helper'
|
||||
import {
|
||||
_APPS,
|
||||
RESOURCE_NAMES,
|
||||
ONEADMIN_ID,
|
||||
DEFAULT_SCHEME,
|
||||
DEFAULT_LANGUAGE,
|
||||
} from 'client/constants'
|
||||
|
||||
const APPS_WITH_VIEWS = [_APPS.sunstone].map((app) => app.toLowerCase())
|
||||
|
||||
@ -126,6 +126,8 @@ export const useAuthApi = () => {
|
||||
changeJwt: (jwt) => dispatch(actions.changeJwt(jwt)),
|
||||
changeAuthUser: (user) => dispatch(actions.changeAuthUser(user)),
|
||||
setErrorMessage: (message) => dispatch(actions.setErrorMessage(message)),
|
||||
changeExternalRedirect: (url) =>
|
||||
dispatch(actions.changeExternalRedirect(url)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ const initial = () => ({
|
||||
user: null,
|
||||
filterPool: FILTER_POOL.ALL_RESOURCES,
|
||||
isLoginInProgress: false,
|
||||
externalRedirect: '',
|
||||
})
|
||||
|
||||
const slice = createSlice({
|
||||
@ -38,6 +39,9 @@ const slice = createSlice({
|
||||
state.isLoginInProgress = isLoginInProgress
|
||||
}
|
||||
},
|
||||
changeExternalRedirect: (state, { payload }) => {
|
||||
state.externalRedirect = payload
|
||||
},
|
||||
changeJwt: (state, { payload }) => {
|
||||
state.jwt = payload
|
||||
},
|
||||
|
@ -144,6 +144,23 @@ const vmApi = oneApi.injectEndpoints({
|
||||
resource: 'VM',
|
||||
}),
|
||||
}),
|
||||
getGuacamoleSessionFile: builder.query({
|
||||
/**
|
||||
* Returns a Guacamole session data.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string} params.id - Virtual machine id
|
||||
* @param {'vnc'|'ssh'|'rdp'} params.type - Connection type
|
||||
* @returns {string} The session token
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = ExtraActions.GUACAMOLE
|
||||
const command = { name, ...ExtraCommands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
}),
|
||||
getGuacamoleSession: builder.query({
|
||||
/**
|
||||
* Returns a Guacamole session.
|
||||
@ -1277,6 +1294,8 @@ export const {
|
||||
useLazyGetVmQuery,
|
||||
useGetGuacamoleSessionQuery,
|
||||
useLazyGetGuacamoleSessionQuery,
|
||||
useGetGuacamoleSessionFileQuery,
|
||||
useLazyGetGuacamoleSessionFileQuery,
|
||||
useGetMonitoringQuery,
|
||||
useLazyGetMonitoringQuery,
|
||||
useGetMonitoringPoolQuery,
|
||||
|
@ -35,6 +35,8 @@ const {
|
||||
defaultCommandVM,
|
||||
defaultTypeCrypto,
|
||||
defaultHash,
|
||||
keysRDP,
|
||||
keysVNC,
|
||||
} = defaults
|
||||
|
||||
const appConfig = getSunstoneConfig()
|
||||
@ -163,9 +165,29 @@ const generateGuacamoleSession = (
|
||||
...connection.connection.settings,
|
||||
protocol: ensuredType,
|
||||
}
|
||||
|
||||
const encodedData = btoa(
|
||||
Object.entries(contentFile)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.map(([key, value]) => {
|
||||
let rtn
|
||||
const keys = type === 'rdp' ? keysRDP : keysVNC
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (keys.hasOwnProperty(key)) {
|
||||
const getValue =
|
||||
value !== null && typeof value !== 'undefined'
|
||||
? value
|
||||
: keys[key].value
|
||||
const parseValue =
|
||||
typeof getValue === 'boolean'
|
||||
? `${+(keys[key].reverse ? !getValue : getValue)}`
|
||||
: `${getValue}`
|
||||
|
||||
rtn = `${keys[key].key}${parseValue}`
|
||||
}
|
||||
|
||||
return rtn
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join('\n')
|
||||
)
|
||||
|
||||
@ -318,7 +340,7 @@ const getRdpSettings = (vmInfo) => {
|
||||
config['disable-glyph-caching'] =
|
||||
nicWithRdp?.RDP_DISABLE_GLYPH_CACHING?.toLowerCase() === 'yes'
|
||||
|
||||
if (config.username && config.password) config.security = 'nla'
|
||||
if (config.username && config.password) config.security = 'rdp'
|
||||
|
||||
return config
|
||||
}
|
||||
|
@ -188,6 +188,53 @@ const defaults = {
|
||||
hookObjectNames: {
|
||||
vn: 'net',
|
||||
},
|
||||
keysRDP: {
|
||||
hostname: { key: 'full address:s:', value: '' },
|
||||
username: { key: 'username:s:', value: '' },
|
||||
password: { key: 'password 51:b:', value: '' },
|
||||
port: { key: 'server port:i:', value: '' },
|
||||
'server-layout': { key: 'keyboard layout:i:', value: '' },
|
||||
'disable-audio': { key: 'audiomode:i:', value: 0 },
|
||||
'enable-audio-input': { key: 'redirectaudiocapture:1:', value: 0 },
|
||||
'enable-wallpaper': {
|
||||
key: 'disable wallpaper:i:',
|
||||
value: 0,
|
||||
reverse: true,
|
||||
},
|
||||
'enable-theming': { key: 'disable themes:i:', value: 0, reverse: true },
|
||||
'enable-font-smoothing': { key: 'allow font smoothing:i:', value: 1 },
|
||||
'enable-full-window-drag': {
|
||||
key: 'disable full window drag:i:',
|
||||
value: 0,
|
||||
reverse: true,
|
||||
},
|
||||
'enable-desktop-composition': {
|
||||
key: 'allow desktop composition:i:',
|
||||
value: 1,
|
||||
},
|
||||
'enable-menu-animations': {
|
||||
key: 'disable menu anims:i:',
|
||||
value: 0,
|
||||
reverse: true,
|
||||
},
|
||||
'disable-bitmap-caching': {
|
||||
key: 'bitmapcachepersistenable:i:',
|
||||
value: 0,
|
||||
reverse: true,
|
||||
},
|
||||
'disable-offscreen-caching': {
|
||||
key: 'offscreen caching:i:',
|
||||
value: 1,
|
||||
reverse: true,
|
||||
},
|
||||
'disable-glyph-caching': { key: 'glyphcache:i:', value: 0, reverse: true },
|
||||
},
|
||||
keysVNC: {
|
||||
hostname: { key: 'Host=', value: '' },
|
||||
port: { key: 'Port=', value: '' },
|
||||
username: { key: 'Username=', value: '' },
|
||||
password: { key: 'Password=', value: '' },
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = defaults
|
||||
|
Loading…
x
Reference in New Issue
Block a user