Merge pull request #1829 from sbenmoussati/bugfix/SDA-4120-main

SDA-4120 Proxy authentication on Browser login flow (#1826)
This commit is contained in:
NguyenTranHoangSym 2023-04-04 09:38:35 +07:00 committed by GitHub
commit b9ca1f6e8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 123 additions and 44 deletions

View File

@ -72,6 +72,24 @@ const broadcastMessage = (method, data) => {
const getBrowserLoginUrl = (pod: string) =>
`${pod}/login/sso/initsso?RelayState=${pod}/client-bff/device-login/index.html?callbackScheme=symphony&action=login`;
const AUTH_STATUS_PATH = '/login/checkauth?type=user';
interface IProxyDetails {
username: string;
password: string;
hostname: string;
retries: number;
}
const proxyDetails: IProxyDetails = {
username: '',
password: '',
hostname: '',
retries: 0,
};
let loginUrl = '';
let formattedPodUrl = '';
let credentialsPromise;
const credentialsPromiseRefHolder: { [key: string]: any } = {};
/**
* Handle API related ipc messages from renderers. Only messages from windows
* we have created are allowed.
@ -368,7 +386,8 @@ ipcMain.on(
}
break;
case apiCmds.unmaximizeMainWindow:
const mainWindow = windowHandler.getMainWindow();
const mainWindow =
windowHandler.getMainWindow() as ICustomBrowserWindow;
if (mainWindow && windowExists(mainWindow)) {
if (mainWindow.isFullScreen()) {
mainWindow.setFullScreen(false);
@ -402,38 +421,14 @@ ipcMain.on(
? userConfigURL
: globalConfigURL;
const { subdomain, domain, tld } = whitelistHandler.parseDomain(podUrl);
const formattedPodUrl = `https://${subdomain}.${domain}${tld}`;
const loginUrl = getBrowserLoginUrl(formattedPodUrl);
formattedPodUrl = `https://${subdomain}.${domain}${tld}`;
loginUrl = getBrowserLoginUrl(formattedPodUrl);
logger.info(
'main-api-handler:',
'check if sso is enabled for the pod',
formattedPodUrl,
);
const response = await fetch(`${formattedPodUrl}${AUTH_STATUS_PATH}`);
const authResponse = (await response.json()) as IAuthResponse;
logger.info('main-api-handler:', 'check auth response', authResponse);
if (
arg.isBrowserLoginEnabled &&
authResponse.authenticationType === 'sso'
) {
logger.info(
'main-api-handler:',
'browser login is enabled - logging in',
loginUrl,
);
await shell.openExternal(loginUrl);
} else {
logger.info(
'main-api-handler:',
'browser login is not enabled - loading main window with',
formattedPodUrl,
);
const mainWebContents = windowHandler.getMainWebContents();
if (mainWebContents && !mainWebContents.isDestroyed()) {
windowHandler.setMainWindowOrigin(formattedPodUrl);
mainWebContents.loadURL(formattedPodUrl);
}
}
loadPodUrl();
break;
case apiCmds.setBroadcastMessage:
if (swiftSearchInstance) {
@ -650,3 +645,70 @@ const logApiCallParams = (arg: any) => {
break;
}
};
const loadPodUrl = (proxyLogin = false) => {
logger.info('loading pod URL. Proxy: ', proxyLogin);
let onLogin = {};
if (proxyLogin) {
onLogin = {
async onLogin(authInfo) {
// this 'authInfo' is the one received by the 'login' event. See https://www.electronjs.org/docs/latest/api/client-request#event-login
proxyDetails.hostname = authInfo.host || authInfo.realm;
await credentialsPromise;
return Promise.resolve({
username: proxyDetails.username,
password: proxyDetails.password,
});
},
};
}
fetch(`${formattedPodUrl}${AUTH_STATUS_PATH}`, onLogin)
.then(async (response) => {
const authResponse = (await response.json()) as IAuthResponse;
logger.info('main-api-handler:', 'check auth response', authResponse);
if (authResponse.authenticationType === 'sso') {
logger.info(
'main-api-handler:',
'browser login is enabled - logging in',
loginUrl,
);
await shell.openExternal(loginUrl);
} else {
logger.info(
'main-api-handler:',
'browser login is not enabled - loading main window with',
formattedPodUrl,
);
const mainWebContents = windowHandler.getMainWebContents();
if (mainWebContents && !mainWebContents.isDestroyed()) {
windowHandler.setMainWindowOrigin(formattedPodUrl);
mainWebContents.loadURL(formattedPodUrl);
}
}
})
.catch(async (error) => {
if (
(error.type === 'proxy' && error.code === 'PROXY_AUTH_FAILED') ||
(error.code === 'ERR_TOO_MANY_RETRIES' && proxyLogin)
) {
credentialsPromise = new Promise((res, _rej) => {
credentialsPromiseRefHolder.resolutionCallback = res;
});
const welcomeWindow =
windowHandler.getMainWindow() as ICustomBrowserWindow;
windowHandler.createBasicAuthWindow(
welcomeWindow,
proxyDetails.hostname,
proxyDetails.retries === 0,
undefined,
(username, password) => {
proxyDetails.username = username;
proxyDetails.password = password;
credentialsPromiseRefHolder.resolutionCallback(true);
loadPodUrl(true);
},
);
proxyDetails.retries += 1;
}
});
};

View File

@ -62,7 +62,7 @@ export default class BasicAuth extends React.Component<{}, IState> {
BASIC_AUTH_NAMESPACE,
)()}
</span>
<span className='hostname'>{hostname}</span>
{hostname && <span className='hostname'>{hostname}</span>}
<span id='credentialsError' className={shouldShowError}>
{i18n.t('Invalid user name/password', BASIC_AUTH_NAMESPACE)()}
</span>

View File

@ -1,4 +1,4 @@
@import "theme";
@import 'theme';
@color-red: red;
@ -10,16 +10,22 @@ html {
body {
margin: 0;
height: 100%;
height: calc(100% - 40px);
margin: 20px;
font-size: 14px;
}
.components-window-root {
height: 100%;
}
.container {
display: flex;
flex-direction: column;
font-family: @font-family;
text-align: center;
padding: 20px;
height: 100%;
justify-content: space-evenly;
}
.container:lang(ja-JP) {
@ -27,12 +33,11 @@ body {
}
span {
padding-top: 10px;
text-align: start;
}
.hostname {
font-size: .9em;
font-size: 0.9em;
}
.credentials-error {
@ -44,28 +49,40 @@ span {
display: block;
}
form {
padding-top: 15px;
}
table {
border-spacing: 5px;
display: flex;
tbody {
display: flex;
flex-direction: column;
width: 100%;
tr {
display: flex;
justify-content: space-between;
text-align: left;
td:last-child {
margin-left: 8px;
display: flex;
flex: 1;
justify-content: flex-end;
}
}
}
}
input {
width: 200px;
width: calc(100% - 8px);
height: 20px;
display: inline-block;
}
button {
width: 80px;
&:lang(fr-FR) {
max-width: 180px;
}
}
.footer {
display: flex;
text-align: center;
padding-top: 25px;
padding-top: 20px;
}
.button-container {