Skip to content
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
5 changes: 5 additions & 0 deletions src/openshift/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,11 @@ export class Cluster extends OpenShiftItem {
);
}

static validateLoginToken(token: string): boolean {
const sha256Regex = new RegExp('^sha256~([A-Za-z0-9_]+)');
return sha256Regex.test(token);
}

@vsCommand('openshift.explorer.login.clipboard')
static async loginUsingClipboardToken(apiEndpointUrl: string, oauthRequestTokenUrl: string): Promise<string | null> {
const clipboard = await Cluster.readFromClipboard();
Expand Down
24 changes: 24 additions & 0 deletions src/webview/cluster/app/clusterView.scss
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,27 @@
.buttonSecondary:disabled {
opacity: 0.4 !important;
}

.buttonRed {
Comment thread
datho7561 marked this conversation as resolved.
white-space: nowrap !important;
display: inline-block !important;
margin-top: 8px !important;
margin-right: 8px !important;
background-color: #EE0000 !important;
color: var(--vscode-button-foreground) !important;
}

.buttonRed:hover {
color: var(--vscode-button-foreground) !important;
background-color: #BE0000 !important;
cursor: pointer !important;
}

.buttonRed:focus {
background-color: #BE0000 !important;
cursor: pointer !important;
}

.buttonRed:disabled {
opacity: 0.4 !important;
}
11 changes: 7 additions & 4 deletions src/webview/cluster/app/sandboxView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Button, CircularProgress, TextField } from '@mui/material';
import IconButton from '@mui/material/IconButton';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import { styled, ThemeProvider } from '@mui/styles';
import { ThemeProvider, styled } from '@mui/styles';
import * as React from 'react';
import PhoneInput from 'react-phone-input-2';
import 'react-phone-input-2/lib/style.css';
Expand Down Expand Up @@ -382,6 +382,9 @@ export default function addSandboxView(props): JSX.Element {
postMessage('sandboxLoginUsingDataInClipboard', {apiEndpointUrl: currentState.apiEndpoint, oauthRequestTokenUrl: `${currentState.oauthTokenEndpoint}/request`});
};

const invalidToken = currentState.errorCode === 'invalidToken';
const loginSandboxTitle = !invalidToken ? 'Login to DevSandbox OpenShift cluster with token from clipboard' : 'Token in clipboard is invalid. Select the Get Token option and copy to clipboard';

return (
<>
{( currentState.action === 'sandboxPageProvisioned' ) && (
Expand Down Expand Up @@ -410,11 +413,11 @@ export default function addSandboxView(props): JSX.Element {
<Tooltip title='Launch your DevSandbox console in browser' placement='bottom'>
<Button variant='contained' className='button' href={currentState.consoleDashboard}>Open Dashboard</Button>
</Tooltip>
<Tooltip title='Copy token from DevSandbox console page in browser' placement='bottom'>
<Tooltip title='Open the DevSandbox console page and copy the login token' placement='bottom'>
<Button variant='contained' className='button' href={`${currentState.oauthTokenEndpoint}/request`}>Get token</Button>
</Tooltip>
<Tooltip title='Login to DevSandbox OpenShift cluster with token from clipboard' placement='bottom'>
<Button variant='contained' className='buttonSecondary' onClick={handleLoginButton}>Login to DevSandbox</Button>
<Tooltip title={loginSandboxTitle} placement='bottom'>
<div style={{ display: 'inline-block', margin: '8px 0px 8px 0px' }}><Button variant='contained' className='buttonRed' disabled={invalidToken} onClick={handleLoginButton}>Login to DevSandbox</Button></div>
</Tooltip>
</div>
)}
Expand Down
23 changes: 22 additions & 1 deletion src/webview/cluster/clusterViewLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,12 @@ async function clusterEditorMessageListener (event: any ): Promise<any> {
} else {
if (signupStatus.status.ready) {
const oauthInfo = await sandboxAPI.getOauthServerInfo(signupStatus.apiEndpoint);
panel.webview.postMessage({action: 'sandboxPageProvisioned', statusInfo: signupStatus.username, consoleDashboard: signupStatus.consoleURL, apiEndpoint: signupStatus.apiEndpoint, oauthTokenEndpoint: oauthInfo.token_endpoint });
let errCode = '';
if (!Cluster.validateLoginToken(await vscode.env.clipboard.readText())) {
errCode = 'invalidToken';
}
panel.webview.postMessage({ action: 'sandboxPageProvisioned', statusInfo: signupStatus.username, consoleDashboard: signupStatus.consoleURL, apiEndpoint: signupStatus.apiEndpoint, oauthTokenEndpoint: oauthInfo.token_endpoint, errorCode: errCode });
pollClipboard(signupStatus);
} else {
// cluster is not ready and the reason is
if (signupStatus.status.verificationRequired) {
Expand Down Expand Up @@ -185,6 +190,22 @@ async function clusterEditorMessageListener (event: any ): Promise<any> {
}
}

async function pollClipboard(signupStatus) {
const oauthInfo = await sandboxAPI.getOauthServerInfo(signupStatus.apiEndpoint);
while (panel) {
const previousContent = await vscode.env.clipboard.readText();
await new Promise(r => setTimeout(r, 500));
const currentContent = await vscode.env.clipboard.readText();
if (previousContent && previousContent !== currentContent) {
let errCode = '';
if (!Cluster.validateLoginToken(currentContent)){
errCode = 'invalidToken';
}
panel.webview.postMessage({action: 'sandboxPageProvisioned', statusInfo: signupStatus.username, consoleDashboard: signupStatus.consoleURL, apiEndpoint: signupStatus.apiEndpoint, oauthTokenEndpoint: oauthInfo.token_endpoint, errorCode: errCode});
}
}
}

export default class ClusterViewLoader {
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
static get extensionPath() {
Expand Down