Skip to content
2 changes: 1 addition & 1 deletion electron/app/js/i18next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ i18n.use(i18nextBackend);

// initialize if not already initialized
if (!i18n.isInitialized) {
i18n.init(i18nextOptions);
i18n['whenReady'] = i18n.init(i18nextOptions);
}

module.exports = i18n;
28 changes: 12 additions & 16 deletions electron/app/js/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
*/
const {app, dialog} = require('electron');
const prompt = require('electron-prompt');
const {getCredentialPassphrase} = require('./promptUtils');
const {copyFile, mkdir, readFile, writeFile} = require('fs/promises');
const path = require('path');
const uuid = require('uuid');
Expand Down Expand Up @@ -691,21 +691,17 @@ async function _createCredentialManager(targetWindow, projectFileJsonContent) {
let credentialStorePolicy = _getProjectCredentialStorePolicy(projectFileJsonContent);
return new Promise((resolve, reject) => {
if (credentialStorePolicy === 'passphrase') {
prompt({
title: i18n.t('dialog-passphrase-prompt-title'),
label: i18n.t('dialog-passphrase-prompt-label'),
inputAttrs: { type: 'password', required: true },
resizable: true,
alwaysOnTop: true
}, targetWindow).then(passphrase => {
if (passphrase) {
const credentialManager = new EncryptedCredentialManager(passphrase);
_setCredentialManager(targetWindow, credentialManager);
resolve(credentialManager);
} else {
reject(new Error('Passphrase is required but the user did not provide one.'));
}
}).catch(err => reject(new Error(`Failed to create passphrase credential manager: ${err}`)));
getCredentialPassphrase(targetWindow)
.then(passphrase => {
if (passphrase) {
const credentialManager = new EncryptedCredentialManager(passphrase);
_setCredentialManager(targetWindow, credentialManager);
resolve(credentialManager);
} else {
reject(new Error('Passphrase is required but the user did not provide one.'));
}
})
.catch(err => reject(new Error(`Failed to create passphrase credential manager: ${err}`)));
} else {
let credentialManager;
if (credentialStorePolicy === 'native') {
Expand Down
26 changes: 26 additions & 0 deletions electron/app/js/prompt/credential-passphrase.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<!--
Copyright (c) 2021, Oracle and/or its affiliates.
Licensed under The Universal Permissive License (UPL), Version 1.0 as shown at https://oss.oracle.com/licenses/upl/
-->
<html lang="en-us">
<head>
<title id="title">...</title>
<link href="prompt.css" rel="stylesheet" />
</head>
<body>
<div id="container">
<form id="form">
<div id="label">...</div>
<div id="data-container">
<input id="data" required type="password">
</div>
<div id="buttons">
<button id="cancel">...</button>
<button type="submit" id="ok">...</button>
</div>
</form>
</div>
<script src="credential-passphrase.js"></script>
</body>
</html>
75 changes: 75 additions & 0 deletions electron/app/js/prompt/credential-passphrase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* @license
* Copyright (c) 2021, Oracle and/or its affiliates.
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
*/
let promptId = null;
let promptOptions = null;

function promptError(error) {
if (error instanceof Error) {
error = error.message;
}

window.api.ipc.sendSync('prompt-error:' + promptId, error);
}

function promptCancel() {
window.api.ipc.sendSync('prompt-post-data:' + promptId, null);
}

function promptSubmit() {
const dataElement = document.querySelector('#data');
const data = dataElement.value;
window.api.ipc.sendSync('prompt-post-data:' + promptId, data);
}

function promptRegister() {
promptId = document.location.hash.replace('#', '');

try {
promptOptions = JSON.parse(window.api.ipc.sendSync('prompt-get-options:' + promptId));
} catch (error) {
return promptError(error);
}

document.querySelector('#form').addEventListener('submit', promptSubmit);
document.querySelector('#cancel').addEventListener('click', promptCancel);

const dataElement = document.querySelector('#data');
dataElement.value = promptOptions.value ? promptOptions.value : '';

dataElement.addEventListener('keyup', event => {
if (event.key === 'Escape') {
promptCancel();
}
});

dataElement.addEventListener('keypress', event => {
if (event.key === 'Enter') {
event.preventDefault();
document.querySelector('#ok').click();
}
});

dataElement.focus();
dataElement.select();

window.api.i18n.ready.then(() => {
document.querySelector('#ok').textContent = window.api.i18n.t('dialog-button-ok');
document.querySelector('#cancel').textContent = window.api.i18n.t('dialog-button-cancel');
document.querySelector('#label').textContent = window.api.i18n.t('dialog-passphrase-prompt-label');
document.querySelector('#title').textContent = window.api.i18n.t('dialog-passphrase-prompt-title');

const height = document.querySelector('body').offsetHeight;
window.api.ipc.sendSync('prompt-size:' + promptId, height);
});
}

window.addEventListener('error', error => {
if (promptId) {
promptError('An error has occurred on the prompt window: \n' + error);
}
});

document.addEventListener('DOMContentLoaded', promptRegister);
28 changes: 28 additions & 0 deletions electron/app/js/prompt/preload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* @license
* Copyright (c) 2021, Oracle and/or its affiliates.
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
*/
const { contextBridge, ipcRenderer } = require('electron');
const i18n = require('../i18next.webui.config');
const osUtils = require('../osUtils');

const language = osUtils.getArgv('--lang');
const i18nReady = i18n.changeLanguage(language);

contextBridge.exposeInMainWorld(
'api',
{
ipc: {
sendSync: (channel, ...args) => {
return ipcRenderer.sendSync(channel, ...args);
}
},
i18n: {
ready: i18nReady,
t: (keys, options) => {
return i18n.t(keys, options);
}
}
}
);
77 changes: 77 additions & 0 deletions electron/app/js/prompt/prompt.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates.
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
*/
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.5em;
color: #333;
background-color: #fff;
padding: 0;
margin: 0;
}

#container {
align-items: center;
justify-content: center;
display: flex;
height: 100%;
overflow: auto;
padding: 1em;
}

#form {
width: 100%;
}

#label, .label {
max-width: 100%;
max-height: 100%;
margin-bottom: .8em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

#data, .data {
box-sizing: border-box;
border-radius: 2px;
background: #fff;
width: 100%;
padding: .4em .5em;
border: 1px solid black;
min-height: 2.5em;
margin: 0 0 1.2em;
}

select#data {
height: 2em;
}

#data-container, .data-container {
text-align: center;
}

#buttons {
text-align: right;
}

#buttons > button,
#buttons > input[type=submit] {
border-radius: 2px;
border: 0;
margin: 0 0 0 .5em;
font-size: .8em;
line-height: 1em;
padding: .6em 1em
}

#ok {
background-color: #3879D9;
color: white;
}

#cancel {
background-color: #DDD;
color: black;
}
107 changes: 107 additions & 0 deletions electron/app/js/promptUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* @license
* Copyright (c) 2021, Oracle and/or its affiliates.
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
*/
const path = require('path');
const { BrowserWindow, ipcMain } = require('electron');

/* global __dirname */

async function getCredentialPassphrase(parentWindow) {
const pageFile = path.join(__dirname, 'prompt', 'credential-passphrase.html');
const preloadFile = path.join(__dirname, 'prompt', 'preload.js');

const WIDTH = 550;
const HEIGHT = 146; // renderer will send IPC to adjust this
const MIN_HEIGHT = 120; // needs to be smaller than content height

return new Promise((resolve, reject) => {

let promptWindow = new BrowserWindow({
width: WIDTH,
height: HEIGHT,
minWidth: WIDTH,
minHeight: MIN_HEIGHT,
resizable: true,
minimizable: false,
fullscreenable: false,
maximizable: false,
parent: parentWindow,
skipTaskbar: true,
alwaysOnTop: false,
useContentSize: true,
modal: Boolean(parentWindow),
menuBarVisible: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
enableRemoteModule: false,
webviewTag: false,
preload: preloadFile
},
});

promptWindow.setMenu(null);
promptWindow.setMenuBarVisibility(false);

const getOptionsListener = event => {
event.returnValue = JSON.stringify({});
};

const sizeListener = (event, value) => {
event.returnValue = null;
promptWindow.setContentSize(WIDTH, value);
promptWindow.center();
};

const id = promptWindow.id.toString();

const cleanup = () => {
ipcMain.removeListener('prompt-get-options:' + id, getOptionsListener);
ipcMain.removeListener('prompt-post-data:' + id, postDataListener);
ipcMain.removeListener('prompt-error:' + id, errorListener);
ipcMain.removeListener('prompt-size:' + id, sizeListener);

if (promptWindow) {
promptWindow.close();
promptWindow = null;
}
};

const postDataListener = (event, value) => {
resolve(value);
event.returnValue = null;
cleanup();
};

const unresponsiveListener = () => {
reject(new Error('Window was unresponsive'));
cleanup();
};

const errorListener = (event, message) => {
reject(new Error(message));
event.returnValue = null;
cleanup();
};

ipcMain.on('prompt-get-options:' + id, getOptionsListener);
ipcMain.on('prompt-post-data:' + id, postDataListener);
ipcMain.on('prompt-error:' + id, errorListener);
ipcMain.on('prompt-size:' + id, sizeListener);
promptWindow.on('unresponsive', unresponsiveListener);

promptWindow.on('closed', () => {
promptWindow = null;
cleanup();
resolve(null);
});

promptWindow.loadFile(pageFile,{hash: id});
});
}

module.exports = {
getCredentialPassphrase
};
2 changes: 0 additions & 2 deletions electron/app/locales/en/electron.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,6 @@
"dialog-invalid-oracle-home-title": "Invalid Oracle Home Directory",
"dialog-invalid-oracle-home-not-specified": "Oracle Home is not specified.",
"dialog-invalid-oracle-home-not-valid": "Specified Oracle Home {{oracleHome}} is not valid.",
"dialog-passphrase-prompt-title": "Credential Encryption Passphrase",
"dialog-passphrase-prompt-label": "Please enter the passphrase to use to encrypt the project credential.",

"dialog-title-closeProject": "Close Project",

Expand Down
3 changes: 3 additions & 0 deletions electron/app/locales/en/webui.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@
"user-settings-dialog-quickstart-skip-label": "Skip Showing Introduction at Startup",
"user-settings-dialog-quickstart-skip-help": "Whether or not to skip showing the WebLogic Kubernetes Toolkit UI Introduction at application startup.",

"dialog-passphrase-prompt-title": "Credential Encryption Passphrase",
"dialog-passphrase-prompt-label": "Please enter the passphrase to use to encrypt the project credential.",

"model-design-coming-soon": "Coming Soon...",

"image-page-hints-createImage": "Create Image",
Expand Down
Loading