mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
Merge remote-tracking branch 'upstream/master' into SEARCH-538
This commit is contained in:
commit
81f14b4916
32
PULL_REQUEST_TEMPLATE.md
Normal file
32
PULL_REQUEST_TEMPLATE.md
Normal file
@ -0,0 +1,32 @@
|
||||
## Description
|
||||
A few sentences describing the overall goals of the pull request's commits. Describe the problem or feature in addition to a link to the [JIRA-ticket](https://perzoinc.atlassian.net/browse/JIRA-ticket)
|
||||
|
||||
|
||||
## Approach
|
||||
How does this change address the problem?
|
||||
- #### Problem with the code:
|
||||
- #### Fix:
|
||||
|
||||
|
||||
## Learning
|
||||
Describe the research stage. Put link to Confluence page if possible. Links to blog posts, patterns, libraries or addons used to solve this problem.
|
||||
|
||||
|
||||
#### Blog Posts
|
||||
- [Alice and Bob](https://en.wikipedia.org/wiki/Alice_and_Bob) Wikipage for the famous placeholder names in engineering literature.
|
||||
|
||||
|
||||
## Related PRs
|
||||
List related PRs against other branches:
|
||||
|
||||
branch | PR
|
||||
------ | ------
|
||||
other_pr_rc | [link]()
|
||||
other_pr_dev | [link]()
|
||||
|
||||
|
||||
## Open Questions if any and Todos
|
||||
- [ ] Unit-Tests
|
||||
- [ ] Documentation
|
||||
- [ ] Automation-Tests
|
||||
When solved, check the box and explain the answer.
|
@ -2,6 +2,10 @@
|
||||
|
||||
# SymphonyElectron
|
||||
|
||||
## About:
|
||||
|
||||
SymphonyElectron is a desktop client of the Symphony Communication Platform built for macOS, Windows 10 and Windows 7.
|
||||
|
||||
## Project Goals:
|
||||
|
||||
Our goal is to improve the performance and development agility of Symphony's desktop wrapper and build a path to support other wrappers by:
|
||||
@ -64,4 +68,6 @@ In order to achieve those goals Symphony is participating and working in close c
|
||||
- Remote logging is enabled for local and production cases and are sent to the backend server via the remote objects
|
||||
|
||||
## Misc notes
|
||||
If desiring to run against server without proper cert use cmd line option: --ignore-certificate-errors
|
||||
- If desiring to run against server without proper cert use cmd line option: --ignore-certificate-errors
|
||||
- To start additional instance with custom data directory (if you want seperate user) use cmd line options: --multiInstance --userDataPath=<path to data dir>
|
||||
- if directory doesn't exist, it will be created
|
||||
|
@ -1,8 +1,9 @@
|
||||
{
|
||||
"url": "https://foundation-dev.symphony.com",
|
||||
"minimizeOnClose" : false,
|
||||
"minimizeOnClose" : true,
|
||||
"launchOnStartup" : true,
|
||||
"alwaysOnTop" : false,
|
||||
"whitelistUrl": "*",
|
||||
"notificationSettings": {
|
||||
"position": "upper-right",
|
||||
"display": ""
|
||||
@ -11,5 +12,9 @@
|
||||
"submitURL": "https://localhost:1127/post",
|
||||
"companyName": "Symphony",
|
||||
"uploadToServer": false
|
||||
},
|
||||
"customFlags": {
|
||||
"authServerWhitelist": "",
|
||||
"authNegotiateDelegateWhitelist": ""
|
||||
}
|
||||
}
|
@ -18,6 +18,10 @@
|
||||
<label for='image'>image url:</label>
|
||||
<input type='text' id='image' value='https://avatars0.githubusercontent.com/u/13243259?v=4&s=460'/>
|
||||
</p>
|
||||
<p>
|
||||
<label for='company'>company:</label>
|
||||
<input type='text' id='company' value='Symphony'/>
|
||||
</p>
|
||||
<p>
|
||||
<label for='flash'>flash:</label>
|
||||
<input type='checkbox' id='flash'/>
|
||||
@ -92,6 +96,7 @@
|
||||
var shouldStick = document.getElementById('sticky').checked;
|
||||
var color = document.getElementById('color').value;
|
||||
var tag = document.getElementById('tag').value;
|
||||
var company = document.getElementById('company').value;
|
||||
|
||||
num++;
|
||||
|
||||
@ -104,7 +109,8 @@
|
||||
data: {
|
||||
hello: 'hello word'
|
||||
},
|
||||
tag: tag
|
||||
tag: tag,
|
||||
company: company
|
||||
});
|
||||
|
||||
notf.addEventListener('click', onclick);
|
||||
|
@ -238,7 +238,6 @@
|
||||
}).catch(function (err) {
|
||||
resultsEl.innerHTML = JSON.stringify(err);
|
||||
});
|
||||
resultsEl.innerHTML = "Merging and Encrypting index completed"
|
||||
});
|
||||
}).catch(function (err) {
|
||||
resultsEl.innerHTML = 'Error: ' + err;
|
||||
|
@ -3,7 +3,7 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>17A405</string>
|
||||
<string>17B48</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
@ -27,17 +27,17 @@
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>9A1004</string>
|
||||
<string>9B55</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>GM</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>17A360</string>
|
||||
<string>17B41</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx10.13</string>
|
||||
<key>DTXcode</key>
|
||||
<string>0901</string>
|
||||
<string>0910</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>9A1004</string>
|
||||
<string>9B55</string>
|
||||
<key>InstallerSectionTitle</key>
|
||||
<string>Pod Settings</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
Binary file not shown.
Binary file not shown.
@ -6,7 +6,7 @@
|
||||
<dict>
|
||||
<key>Resources/Base.lproj/MyInstallerPane.nib</key>
|
||||
<data>
|
||||
TF/AqkGdS25ttnHMS1l76ES81/w=
|
||||
GZWN47BPj6b6mD6MnSVH/Qn0m3s=
|
||||
</data>
|
||||
<key>Resources/InstallerSections.plist</key>
|
||||
<data>
|
||||
@ -37,11 +37,11 @@
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
TF/AqkGdS25ttnHMS1l76ES81/w=
|
||||
GZWN47BPj6b6mD6MnSVH/Qn0m3s=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
gxXMI4SoTYE7jYkP5QJ7i804TUXR4x8LGSh99n9qers=
|
||||
cJ/kr1HEozYL0YeZriWDtnOL1NEd22vHzH5WGp1T3hk=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/InstallerSections.plist</key>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13196" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13196"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13529"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -59,7 +59,7 @@
|
||||
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XPe-yO-v9Y">
|
||||
<rect key="frame" x="194" y="85" width="22" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" inset="2" id="uvu-EE-3sp">
|
||||
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="uvu-EE-3sp">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
|
@ -31,7 +31,7 @@
|
||||
}
|
||||
|
||||
// Now, validate the url against a url regex
|
||||
NSString *regex = @"^((?:http:\/\/)|(?:https:\/\/))(www.)?((?:[a-zA-Z0-9]+\.[a-z]{3})|(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?::\d+)?))([\/a-zA-Z0-9\.]*)$";
|
||||
NSString *regex = @"^(https:\/\/|http:\/\/)(www.)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}(:[0-9]{1,5})?(\/[a-zA-Z0-9-_.+!*'(),;/?:@=&$]*)?$";
|
||||
NSPredicate *podUrlTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
|
||||
if ([podUrlTest evaluateWithObject:podUrl]) {
|
||||
return YES;
|
||||
@ -48,23 +48,23 @@
|
||||
|
||||
NSString *podUrl = [_podUrlTextBox stringValue];
|
||||
|
||||
// By default, set autoLaunchOnStart to true
|
||||
// By default, set autoLaunchOnStart and minimizeOnClose to true
|
||||
NSString *autoLaunchOnStart = @"true";
|
||||
NSString *minimizeOnClose = @"true";
|
||||
|
||||
// If the checkbox is changed, set the auto launch value accordingly
|
||||
if ([_autoLaunchCheckBox state] == 0) {
|
||||
autoLaunchOnStart = @"false";
|
||||
}
|
||||
|
||||
// By default, set minimizeOnClose and alwaysOnTop to false
|
||||
NSString *minimizeOnClose = @"false";
|
||||
NSString *alwaysOnTop = @"false\n";
|
||||
|
||||
// If the checkbox is changed, set the minimize on close value accordingly
|
||||
if ([_minimizeOnCloseCheckBox state] == 1) {
|
||||
minimizeOnClose = @"true";
|
||||
if ([_minimizeOnCloseCheckBox state] == 0) {
|
||||
minimizeOnClose = @"false";
|
||||
}
|
||||
|
||||
// By default, set alwaysOnTop to false
|
||||
NSString *alwaysOnTop = @"false\n";
|
||||
|
||||
// If the checkbox is changed, set the always on top value accordingly
|
||||
if ([_alwaysOnTopCheckBox state] == 1) {
|
||||
alwaysOnTop = @"true\n";
|
||||
|
@ -4,24 +4,40 @@
|
||||
tempFilePath='/tmp/sym_settings.txt'
|
||||
installPath="$2"
|
||||
configPath="/Symphony.app/Contents/config/Symphony.config"
|
||||
newPath=$installPath$configPath
|
||||
newPath=${installPath}${configPath}
|
||||
|
||||
## Get Symphony Settings from the temp file ##
|
||||
pod_url=$(sed -n '1p' $tempFilePath);
|
||||
pod_url=$(sed -n '1p' ${tempFilePath});
|
||||
minimize_on_close=$(sed -n '2p' '/tmp/sym_settings.txt');
|
||||
launch_on_startup=$(sed -n '3p' '/tmp/sym_settings.txt');
|
||||
always_on_top=$(sed -n '4p' '/tmp/sym_settings.txt');
|
||||
|
||||
if [ "$pod_url" == "" ]; then
|
||||
pod_url="https://corporate.symphony.com"
|
||||
fi
|
||||
|
||||
if [ "$minimize_on_close" == "" ]; then
|
||||
minimize_on_close=true;
|
||||
fi
|
||||
|
||||
if [ "$launch_on_startup" == "" ]; then
|
||||
launch_on_startup=true;
|
||||
fi
|
||||
|
||||
if [ "$always_on_top" == "" ]; then
|
||||
always_on_top=false;
|
||||
fi
|
||||
|
||||
## Replace the default settings with the user selected settings ##
|
||||
sed -i "" -E "s#\"url\" ?: ?\".*\"#\"url\"\: \"$pod_url\"#g" $newPath
|
||||
sed -i "" -E "s#\"minimizeOnClose\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"minimizeOnClose\":\ $minimize_on_close#g" $newPath
|
||||
sed -i "" -E "s#\"alwaysOnTop\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"alwaysOnTop\":\ $always_on_top#g" $newPath
|
||||
sed -i "" -E "s#\"launchOnStartup\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"launchOnStartup\":\ $launch_on_startup#g" $newPath
|
||||
sed -i "" -E "s#\"url\" ?: ?\".*\"#\"url\"\: \"$pod_url\"#g" ${newPath}
|
||||
sed -i "" -E "s#\"minimizeOnClose\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"minimizeOnClose\":\ $minimize_on_close#g" ${newPath}
|
||||
sed -i "" -E "s#\"alwaysOnTop\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"alwaysOnTop\":\ $always_on_top#g" ${newPath}
|
||||
sed -i "" -E "s#\"launchOnStartup\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"launchOnStartup\":\ $launch_on_startup#g" ${newPath}
|
||||
|
||||
## Remove the temp settings file created ##
|
||||
rm -f $tempFilePath
|
||||
rm -f ${tempFilePath}
|
||||
|
||||
## For launching symphony with sandbox enabled, create a shell script that is used as the launch point for the app
|
||||
EXEC_PATH=$installPath/Symphony.app/Contents/MacOS
|
||||
exec $EXEC_PATH/Symphony --install $newPath $launch_on_startup
|
||||
chmod 755 $EXEC_PATH/Symphony
|
||||
EXEC_PATH=${installPath}/Symphony.app/Contents/MacOS
|
||||
exec ${EXEC_PATH}/Symphony --install ${newPath} ${launch_on_startup}
|
||||
chmod 755 ${EXEC_PATH}/Symphony
|
||||
|
3
installer/mac/preinstall.sh
Normal file
3
installer/mac/preinstall.sh
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
sudo killall Symphony
|
||||
sudo rm -rf /Applications/Symphony.app
|
@ -450,6 +450,13 @@
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>PREINSTALL_PATH</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>preinstall.sh</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>RESOURCES</key>
|
||||
<array/>
|
||||
</dict>
|
||||
|
@ -24,6 +24,7 @@
|
||||
<ROW Property="CTRLS" Value="2"/>
|
||||
<ROW Property="DialogBitmap" Value="dialog" MultiBuildValue="DefaultBuild:Tabloid.jpg" Type="1" MsiKey="DialogBitmap"/>
|
||||
<ROW Property="INVALID_POD_URL" Value="valid" Type="4"/>
|
||||
<ROW Property="MINIMIZE_ON_CLOSE" Value="true"/>
|
||||
<ROW Property="MINIMIZE_ON_CLOSE_LABEL" Value="false" Type="4"/>
|
||||
<ROW Property="Manufacturer" Value="Symphony"/>
|
||||
<ROW Property="POD_URL" Value="https://corporate.symphony.com" Type="4"/>
|
||||
@ -552,7 +553,7 @@
|
||||
<ROW Action="AI_TxtUpdaterRollback" Type="11521" Source="TxtUpdater.dll" Target="OnTxtUpdaterRollback" WithoutSeq="true"/>
|
||||
<ROW Action="KillParagon" Type="1" Source="aicustact.dll" Target="StopProcess" Options="1" AdditionalSeq="AI_DATA_SETTER_2"/>
|
||||
<ROW Action="KillRenderer" Type="1" Source="aicustact.dll" Target="StopProcess" Options="1" AdditionalSeq="AI_DATA_SETTER"/>
|
||||
<ROW Action="PodUrlValidation" Type="37" Target="Script Text" TargetUnformatted="// First, check if the protocol is part of the url, if not, prepend it var prefix = "https://"; if (Session.Property("POD_URL").substr(0, prefix.length) !== prefix) { 	Session.Property("POD_URL") = prefix + Session.Property("POD_URL"); } // Check if the entered pod url is valid var podUrlRE = /^((?:http:\/\/)|(?:https:\/\/))(www.)?((?:[a-zA-Z0-9]+\.[a-z]{3})|(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?::\d+)?))([\/a-zA-Z0-9\.]*)$/; var podUrlTest = podUrlRE.test(Session.Property("POD_URL")); if (!podUrlTest) { 	Session.Property("INVALID_POD_URL") = "invalid"; } else { 	Session.Property("INVALID_POD_URL") = "valid"; } // By default, we set all the values to false and change based on conditions Session.Property("ALWAYS_ON_TOP_LABEL") = "false"; Session.Property("AUTO_START_LABEL") = "false"; Session.Property("MINIMIZE_ON_CLOSE_LABEL") = "false"; // If always on top is checked in the checkbox, set the label value to true if (Session.Property("ALWAYS_ON_TOP") && Session.Property("ALWAYS_ON_TOP") === "true") { Session.Property("ALWAYS_ON_TOP_LABEL") = "true"; } // If launch on startup is checked in the checkbox, set the label value to true if (Session.Property("MINIMIZE_ON_CLOSE") && Session.Property("MINIMIZE_ON_CLOSE") === "true") { Session.Property("MINIMIZE_ON_CLOSE_LABEL") = "true"; } // If minimise on close is checked in the checkbox, set the label value to true if (Session.Property("AUTO_START") && Session.Property("AUTO_START") === "true") { Session.Property("AUTO_START_LABEL") = "true"; }" WithoutSeq="true"/>
|
||||
<ROW Action="PodUrlValidation" Type="37" Target="Script Text" TargetUnformatted="// First, check if the protocol is part of the url, if not, prepend it var prefix1 = "https://"; var prefix2 = "http://"; if (Session.Property("POD_URL").substr(0, prefix1.length) !== prefix1 && Session.Property("POD_URL").substr(0, prefix2.length) !== prefix2) { Session.Property("POD_URL") = prefix1 + Session.Property("POD_URL"); } // Check if the entered pod url is valid var podUrlRE = /^(https:\/\/|http:\/\/)(www.)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}(:[0-9]{1,5})?(\/[a-zA-Z0-9-_.+!*'(),;/?:@=&$]*)?$/; var podUrlTest = podUrlRE.test(Session.Property("POD_URL")); if (!podUrlTest) { 	Session.Property("INVALID_POD_URL") = "invalid"; } else { 	Session.Property("INVALID_POD_URL") = "valid"; } // By default, we set all the values to false and change based on conditions Session.Property("ALWAYS_ON_TOP_LABEL") = "false"; Session.Property("AUTO_START_LABEL") = "false"; Session.Property("MINIMIZE_ON_CLOSE_LABEL") = "false"; // If always on top is checked in the checkbox, set the label value to true if (Session.Property("ALWAYS_ON_TOP") && Session.Property("ALWAYS_ON_TOP") === "true") { Session.Property("ALWAYS_ON_TOP_LABEL") = "true"; } // If launch on startup is checked in the checkbox, set the label value to true if (Session.Property("MINIMIZE_ON_CLOSE") && Session.Property("MINIMIZE_ON_CLOSE") === "true") { Session.Property("MINIMIZE_ON_CLOSE_LABEL") = "true"; } // If minimise on close is checked in the checkbox, set the label value to true if (Session.Property("AUTO_START") && Session.Property("AUTO_START") === "true") { Session.Property("AUTO_START_LABEL") = "true"; }" WithoutSeq="true"/>
|
||||
<ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]" MultiBuildTarget="DefaultBuild:[AI_UserProgramFiles][Manufacturer]\[ProductName]"/>
|
||||
<ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]"/>
|
||||
<ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
|
||||
|
@ -26,6 +26,10 @@
|
||||
font-size: .9em;
|
||||
}
|
||||
|
||||
.credentials-error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
form {
|
||||
padding-top: 15px;
|
||||
}
|
||||
@ -60,6 +64,7 @@
|
||||
<div class="container">
|
||||
<span>Please provide your login credentials for:</span>
|
||||
<span id="hostname" class="hostname">hostname</span>
|
||||
<span id="credentialsError" class="credentials-error">Invalid user name/password</span>
|
||||
<form id="basicAuth" name="Basic Auth" action="Login">
|
||||
<table class="form">
|
||||
<tbody>
|
||||
|
@ -7,6 +7,7 @@ const path = require('path');
|
||||
const fs = require('fs');
|
||||
const log = require('../log.js');
|
||||
const logLevels = require('../enums/logLevels.js');
|
||||
const { isMac } = require('../utils/misc');
|
||||
|
||||
let basicAuthWindow;
|
||||
|
||||
@ -14,7 +15,7 @@ const local = {};
|
||||
|
||||
let windowConfig = {
|
||||
width: 360,
|
||||
height: 270,
|
||||
height: isMac ? 270 : 295,
|
||||
show: false,
|
||||
modal: true,
|
||||
autoHideMenuBar: true,
|
||||
@ -45,14 +46,20 @@ function getTemplatePath() {
|
||||
* Opens the basic auth window for authentication
|
||||
* @param {String} windowName - name of the window upon which this window should show
|
||||
* @param {String} hostname - name of the website that requires authentication
|
||||
* @param {boolean} isValidCredentials - false if invalid username or password
|
||||
* @param {Function} clearSettings
|
||||
* @param {Function} callback
|
||||
*/
|
||||
function openBasicAuthWindow(windowName, hostname, callback) {
|
||||
function openBasicAuthWindow(windowName, hostname, isValidCredentials, clearSettings, callback) {
|
||||
|
||||
// Register callback function
|
||||
if (typeof callback === 'function') {
|
||||
local.authCallback = callback;
|
||||
}
|
||||
// Register close function
|
||||
if (typeof clearSettings === 'function') {
|
||||
local.clearSettings = clearSettings;
|
||||
}
|
||||
|
||||
// This prevents creating multiple instances of the
|
||||
// basic auth window
|
||||
@ -89,6 +96,7 @@ function openBasicAuthWindow(windowName, hostname, callback) {
|
||||
|
||||
basicAuthWindow.webContents.on('did-finish-load', () => {
|
||||
basicAuthWindow.webContents.send('hostname', hostname);
|
||||
basicAuthWindow.webContents.send('isValidCredentials', isValidCredentials);
|
||||
});
|
||||
|
||||
basicAuthWindow.on('close', () => {
|
||||
@ -103,14 +111,12 @@ function openBasicAuthWindow(windowName, hostname, callback) {
|
||||
ipc.on('login', (event, args) => {
|
||||
if (typeof args === 'object' && typeof local.authCallback === 'function') {
|
||||
local.authCallback(args.username, args.password);
|
||||
basicAuthWindow.close();
|
||||
closeAuthWindow(false);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('close-basic-auth', () => {
|
||||
if (basicAuthWindow) {
|
||||
basicAuthWindow.close();
|
||||
}
|
||||
closeAuthWindow(true);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -120,6 +126,19 @@ function destroyWindow() {
|
||||
basicAuthWindow = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to close the auth window
|
||||
* @param {boolean} clearSettings - Whether to clear the auth settings
|
||||
*/
|
||||
function closeAuthWindow(clearSettings) {
|
||||
if (clearSettings && typeof local.clearSettings === 'function') {
|
||||
local.clearSettings();
|
||||
}
|
||||
|
||||
if (basicAuthWindow) {
|
||||
basicAuthWindow.close();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
openBasicAuthWindow: openBasicAuthWindow
|
||||
|
@ -52,4 +52,15 @@ ipc.on('hostname', (event, host) => {
|
||||
if (hostname){
|
||||
hostname.innerHTML = host || 'unknown';
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Triggered if user credentials are invalid
|
||||
*/
|
||||
ipc.on('isValidCredentials', (event, isValidCredentials) => {
|
||||
let credentialsError = document.getElementById('credentialsError');
|
||||
|
||||
if (credentialsError){
|
||||
credentialsError.style.display = isValidCredentials ? 'none' : 'block'
|
||||
}
|
||||
});
|
35
js/config.js
35
js/config.js
@ -382,6 +382,34 @@ function clearCachedConfigs() {
|
||||
globalConfig = null;
|
||||
}
|
||||
|
||||
function readConfigFileSync() {
|
||||
|
||||
let configPath;
|
||||
let globalConfigFileName = path.join('config', configFileName);
|
||||
if (isDevEnv) {
|
||||
// for dev env, get config file from asar
|
||||
configPath = path.join(app.getAppPath(), globalConfigFileName);
|
||||
} else {
|
||||
// for non-dev, config file is placed by installer relative to exe.
|
||||
// this is so the config can be easily be changed post install.
|
||||
let execPath = path.dirname(app.getPath('exe'));
|
||||
// for mac exec is stored in subdir, for linux/windows config
|
||||
// dir is in the same location.
|
||||
configPath = path.join(execPath, isMac ? '..' : '', globalConfigFileName);
|
||||
}
|
||||
|
||||
let data = fs.readFileSync(configPath);
|
||||
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch (err) {
|
||||
console.log("parsing config failed", err);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
configFileName,
|
||||
@ -395,6 +423,11 @@ module.exports = {
|
||||
|
||||
// items below here are only exported for testing, do NOT use!
|
||||
saveUserConfig,
|
||||
clearCachedConfigs
|
||||
clearCachedConfigs,
|
||||
|
||||
readConfigFileSync,
|
||||
|
||||
// use only if you specifically need to read global config fields
|
||||
getGlobalConfigField,
|
||||
|
||||
};
|
||||
|
@ -12,7 +12,8 @@
|
||||
// renderer process, this will have to do. See github issue posted here to
|
||||
// electron: https://github.com/electron/electron/issues/9312
|
||||
|
||||
const { ipcRenderer } = require('electron');
|
||||
const { ipcRenderer, remote } = require('electron');
|
||||
const { isWindowsOS } = require('../utils/misc');
|
||||
|
||||
let nextId = 0;
|
||||
let includes = [].includes;
|
||||
@ -52,6 +53,17 @@ function getSources(options, callback) {
|
||||
};
|
||||
}
|
||||
|
||||
if (isWindowsOS) {
|
||||
/**
|
||||
* Sets the captureWindow to false if Desktop composition
|
||||
* is disabled otherwise true
|
||||
*
|
||||
* Setting captureWindow to false returns only screen sources
|
||||
* @type {boolean}
|
||||
*/
|
||||
captureWindow = remote.systemPreferences.isAeroGlassEnabled();
|
||||
}
|
||||
|
||||
id = getNextId();
|
||||
ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, updatedOptions.thumbnailSize, id);
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
const electron = require('electron');
|
||||
|
||||
const basicAuth = require('../basicAuth');
|
||||
let currentAuthURL;
|
||||
let tries = 0;
|
||||
|
||||
/**
|
||||
* Having a proxy or hosts that requires authentication will allow user to
|
||||
@ -12,14 +14,33 @@ electron.app.on('login', (event, webContents, request, authInfo, callback) => {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
// This check is to determine whether the request is for the same
|
||||
// host if so then increase the login tries from which we can
|
||||
// display invalid credentials
|
||||
if (currentAuthURL !== request.url) {
|
||||
currentAuthURL = request.url;
|
||||
} else {
|
||||
tries++
|
||||
}
|
||||
|
||||
// name of the host to display
|
||||
let hostname = authInfo.host || authInfo.realm;
|
||||
let browserWin = electron.BrowserWindow.fromWebContents(webContents);
|
||||
let windowName = browserWin.winName || '';
|
||||
|
||||
/**
|
||||
* Method that resets currentAuthURL and tries
|
||||
* if user closes the auth window
|
||||
*/
|
||||
function clearSettings() {
|
||||
callback();
|
||||
currentAuthURL = '';
|
||||
tries = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an electron modal window in which
|
||||
* user can enter credentials fot the host
|
||||
*/
|
||||
basicAuth.openBasicAuthWindow(windowName, hostname, callback);
|
||||
basicAuth.openBasicAuthWindow(windowName, hostname, tries === 0, clearSettings, callback);
|
||||
});
|
||||
|
71
js/main.js
71
js/main.js
@ -11,7 +11,7 @@ const AutoLaunch = require('auto-launch');
|
||||
const urlParser = require('url');
|
||||
|
||||
// Local Dependencies
|
||||
const {getConfigField, updateUserConfigWin, updateUserConfigMac} = require('./config.js');
|
||||
const {getConfigField, updateUserConfigWin, updateUserConfigMac, readConfigFileSync} = require('./config.js');
|
||||
const {setCheckboxValues} = require('./menus/menuTemplate.js');
|
||||
const { isMac, isDevEnv } = require('./utils/misc.js');
|
||||
const protocolHandler = require('./protocolHandler');
|
||||
@ -81,8 +81,10 @@ const shouldQuit = app.makeSingleInstance((argv) => {
|
||||
processProtocolAction(argv);
|
||||
});
|
||||
|
||||
// quit if another instance is already running, ignore for dev env
|
||||
if (!isDevEnv && shouldQuit) {
|
||||
let allowMultiInstance = getCmdLineArg(process.argv, '--multiInstance', true) || isDevEnv;
|
||||
|
||||
// quit if another instance is already running, ignore for dev env or if app was started with multiInstance flag
|
||||
if (!allowMultiInstance && shouldQuit) {
|
||||
app.quit();
|
||||
}
|
||||
|
||||
@ -103,6 +105,39 @@ if (isMac) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets chrome authentication flags in electron
|
||||
*/
|
||||
function setChromeFlags() {
|
||||
|
||||
log.send(logLevels.INFO, 'checking if we need to set custom chrome flags!');
|
||||
|
||||
// Read the config parameters synchronously
|
||||
let config = readConfigFileSync();
|
||||
|
||||
// If we cannot find any config, just skip setting any flags
|
||||
if (config && config !== null && config.customFlags) {
|
||||
|
||||
log.send(logLevels.INFO, 'Chrome flags config found!');
|
||||
|
||||
// If we cannot find the authServerWhitelist config, move on
|
||||
if (config.customFlags.authServerWhitelist && config.customFlags.authServerWhitelist !== "") {
|
||||
log.send(logLevels.INFO, 'Setting auth server whitelist flag');
|
||||
app.commandLine.appendSwitch('auth-server-whitelist', config.customFlags.authServerWhitelist);
|
||||
}
|
||||
|
||||
// If we cannot find the authNegotiateDelegateWhitelist config, move on
|
||||
if (config.customFlags.authNegotiateDelegateWhitelist && config.customFlags.authNegotiateDelegateWhitelist !== "") {
|
||||
log.send(logLevels.INFO, 'Setting auth negotiate delegate whitelist flag');
|
||||
app.commandLine.appendSwitch('auth-negotiate-delegate-whitelist', config.customFlags.authNegotiateDelegateWhitelist);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Set the chrome flags
|
||||
setChromeFlags();
|
||||
|
||||
/**
|
||||
* This method will be called when Electron has finished
|
||||
* initialization and is ready to create browser windows.
|
||||
@ -176,6 +211,12 @@ function setupThenOpenMainWindow() {
|
||||
// allows installer to launch app and set appropriate global / user config params.
|
||||
let hasInstallFlag = getCmdLineArg(process.argv, '--install', true);
|
||||
let perUserInstall = getCmdLineArg(process.argv, '--peruser', true);
|
||||
let customDataArg = getCmdLineArg(process.argv, '--userDataPath=', false);
|
||||
|
||||
if (customDataArg && customDataArg.split('=').length > 1) {
|
||||
let customDataFolder = customDataArg.split('=')[1];
|
||||
app.setPath('userData', customDataFolder);
|
||||
}
|
||||
if (!isMac && hasInstallFlag) {
|
||||
getConfigField('launchOnStartup')
|
||||
.then(setStartup)
|
||||
@ -193,9 +234,8 @@ function setupThenOpenMainWindow() {
|
||||
let launchOnStartup = process.argv[3];
|
||||
// We wire this in via the post install script
|
||||
// to get the config file path where the app is installed
|
||||
let appGlobalConfigPath = process.argv[2];
|
||||
setStartup(launchOnStartup)
|
||||
.then(() => updateUserConfigMac(appGlobalConfigPath))
|
||||
.then(updateUserConfigMac)
|
||||
.then(app.quit)
|
||||
.catch(app.quit);
|
||||
return;
|
||||
@ -214,18 +254,15 @@ function setupThenOpenMainWindow() {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function setStartup(lStartup) {
|
||||
return symphonyAutoLauncher.isEnabled()
|
||||
.then(function(isEnabled) {
|
||||
if (!isEnabled && lStartup) {
|
||||
return symphonyAutoLauncher.enable();
|
||||
}
|
||||
|
||||
if (isEnabled && !lStartup) {
|
||||
return symphonyAutoLauncher.disable();
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
return new Promise((resolve) => {
|
||||
let launchOnStartup = (lStartup === 'true');
|
||||
if (launchOnStartup) {
|
||||
symphonyAutoLauncher.enable();
|
||||
return resolve();
|
||||
}
|
||||
symphonyAutoLauncher.disable();
|
||||
return resolve();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,7 +65,23 @@ const template = [{
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Open Crashes Directory',
|
||||
label: 'Set Downloads Directory',
|
||||
click() {
|
||||
electron.dialog.showOpenDialog({
|
||||
title: 'Select Downloads Directory',
|
||||
buttonLabel: 'Select',
|
||||
properties: ['openDirectory', 'createDirectory']
|
||||
}, (filePaths) => {
|
||||
if (!filePaths || !Array.isArray(filePaths) || filePaths.length < 1) {
|
||||
return;
|
||||
}
|
||||
updateConfigField('downloadsDirectory', filePaths[0]);
|
||||
eventEmitter.emit('setDownloadsDirectory', filePaths[0]);
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Open Crashes Directory',
|
||||
click() {
|
||||
const crashesDirectory = electron.crashReporter.getCrashesDirectory() + '/completed';
|
||||
electron.shell.showItemInFolder(crashesDirectory);
|
||||
|
@ -23,7 +23,7 @@ function setStyle(config) {
|
||||
let image = notiDoc.getElementById('image');
|
||||
let logo = notiDoc.getElementById('symphony-logo');
|
||||
let title = notiDoc.getElementById('title');
|
||||
let pod = notiDoc.getElementById('pod');
|
||||
let company = notiDoc.getElementById('company');
|
||||
let message = notiDoc.getElementById('message');
|
||||
let close = notiDoc.getElementById('close');
|
||||
|
||||
@ -45,7 +45,7 @@ function setStyle(config) {
|
||||
|
||||
setStyleOnDomElement(config.defaultStyleTitle, title);
|
||||
|
||||
setStyleOnDomElement(config.defaultStylePod, pod);
|
||||
setStyleOnDomElement(config.defaultStyleCompany, company);
|
||||
|
||||
setStyleOnDomElement(config.defaultStyleText, message);
|
||||
|
||||
@ -79,7 +79,13 @@ function setContents(event, notificationObj) {
|
||||
|
||||
let notiDoc = window.document;
|
||||
|
||||
// All the required DOM elements to update the content
|
||||
let container = notiDoc.getElementById('container');
|
||||
let titleDoc = notiDoc.getElementById('title');
|
||||
let companyDoc = notiDoc.getElementById('company');
|
||||
let messageDoc = notiDoc.getElementById('message');
|
||||
let imageDoc = notiDoc.getElementById('image');
|
||||
let closeButton = notiDoc.getElementById('close');
|
||||
|
||||
if (notificationObj.color) {
|
||||
container.style.backgroundColor = notificationObj.color;
|
||||
@ -88,13 +94,9 @@ function setContents(event, notificationObj) {
|
||||
if (notificationObj.color.match(whiteColorRegExp)) {
|
||||
logo.src = './assets/symphony-logo-black.png';
|
||||
} else {
|
||||
let title = notiDoc.getElementById('title');
|
||||
let pod = notiDoc.getElementById('pod');
|
||||
let message = notiDoc.getElementById('message');
|
||||
|
||||
message.style.color = '#ffffff';
|
||||
title.style.color = '#ffffff';
|
||||
pod.style.color = notificationObj.color;
|
||||
messageDoc.style.color = '#ffffff';
|
||||
titleDoc.style.color = '#ffffff';
|
||||
companyDoc.style.color = notificationObj.color;
|
||||
logo.src = './assets/symphony-logo-white.png';
|
||||
}
|
||||
}
|
||||
@ -111,24 +113,26 @@ function setContents(event, notificationObj) {
|
||||
}
|
||||
|
||||
// Title
|
||||
let titleDoc = notiDoc.getElementById('title');
|
||||
titleDoc.innerHTML = notificationObj.title || '';
|
||||
|
||||
// message
|
||||
let messageDoc = notiDoc.getElementById('message');
|
||||
messageDoc.innerHTML = notificationObj.text || '';
|
||||
|
||||
// Image
|
||||
let imageDoc = notiDoc.getElementById('image');
|
||||
if (notificationObj.image) {
|
||||
imageDoc.src = notificationObj.image;
|
||||
} else {
|
||||
setStyleOnDomElement({ display: 'none'}, imageDoc);
|
||||
}
|
||||
|
||||
const winId = notificationObj.windowId;
|
||||
// Company
|
||||
if (notificationObj.company) {
|
||||
companyDoc.innerHTML = notificationObj.company
|
||||
} else {
|
||||
messageDoc.style.marginTop = '15px';
|
||||
}
|
||||
|
||||
let closeButton = notiDoc.getElementById('close');
|
||||
const winId = notificationObj.windowId;
|
||||
|
||||
// note: use onclick because we only want one handler, for case
|
||||
// when content gets overwritten by notf with same tag
|
||||
|
@ -7,7 +7,7 @@
|
||||
</div>
|
||||
<div id="header">
|
||||
<span id="title"></span>
|
||||
<span id="pod"></span>
|
||||
<span id="company"></span>
|
||||
<span id="message"></span>
|
||||
</div>
|
||||
<div id="picture">
|
||||
|
@ -76,7 +76,7 @@ let config = {
|
||||
defaultStyleHeader: {
|
||||
width: 245,
|
||||
minWidth: 230,
|
||||
margin: "12px 10px"
|
||||
margin: "10px 10px"
|
||||
},
|
||||
defaultStyleImage: {
|
||||
height: 43,
|
||||
@ -102,7 +102,7 @@ let config = {
|
||||
webkitLineClamp: 1,
|
||||
webkitBoxOrient: 'vertical',
|
||||
},
|
||||
defaultStylePod: {
|
||||
defaultStyleCompany: {
|
||||
fontFamily: 'sans-serif',
|
||||
fontSize: 11,
|
||||
color: '#adadad',
|
||||
@ -116,7 +116,7 @@ let config = {
|
||||
fontFamily: 'sans-serif',
|
||||
fontSize: 12,
|
||||
color: '#4a4a4a',
|
||||
marginTop: 12,
|
||||
marginTop: 5,
|
||||
overflow: 'hidden',
|
||||
display: '-webkit-box',
|
||||
webkitLineClamp: 1,
|
||||
|
@ -41,6 +41,7 @@ class Notify {
|
||||
color: options.color,
|
||||
tag: options.tag,
|
||||
sticky: options.sticky || false,
|
||||
company: options.company,
|
||||
onShowFunc: onShow.bind(this),
|
||||
onClickFunc: onClick.bind(this),
|
||||
onCloseFunc: onClose.bind(this),
|
||||
|
@ -235,12 +235,7 @@ class Search {
|
||||
* to the main user index
|
||||
*/
|
||||
encryptIndex(key) {
|
||||
return this.crypto.encryption(key).then(() => {
|
||||
return 'Success'
|
||||
}).catch((e) => {
|
||||
log.send(logLevels.ERROR, 'Encrypting the index folder failed ->' + e);
|
||||
return (new Error(e));
|
||||
});
|
||||
return this.crypto.encryption(key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,7 +92,7 @@ function readFile(userId, resolve, reject) {
|
||||
}
|
||||
|
||||
/**
|
||||
* If the config as no object for the provided userId this function
|
||||
* If the config has no object for the provided userId this function
|
||||
* creates an empty object with the key as the userId
|
||||
* @param userId
|
||||
* @param oldConfig
|
||||
@ -112,7 +112,7 @@ function createUser(userId, oldConfig) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This function the creates the config
|
||||
* This function creates the config
|
||||
* file if not present
|
||||
* @param userId
|
||||
* @param data
|
||||
|
@ -4,11 +4,13 @@ const isDevEnv = process.env.ELECTRON_DEV ?
|
||||
process.env.ELECTRON_DEV.trim().toLowerCase() === 'true' : false;
|
||||
|
||||
const isMac = (process.platform === 'darwin');
|
||||
const isWindowsOS = (process.platform === 'win32');
|
||||
|
||||
const isNodeEnv = !!process.env.NODE_ENV;
|
||||
|
||||
module.exports = {
|
||||
isDevEnv: isDevEnv,
|
||||
isMac: isMac,
|
||||
isWindowsOS: isWindowsOS,
|
||||
isNodeEnv: isNodeEnv
|
||||
};
|
||||
|
120
js/utils/whitelistHandler.js
Normal file
120
js/utils/whitelistHandler.js
Normal file
@ -0,0 +1,120 @@
|
||||
'use strict';
|
||||
|
||||
const { getGlobalConfigField } = require('./../config.js');
|
||||
const parseDomain = require('parse-domain');
|
||||
const isEqual = require('lodash.isequal');
|
||||
|
||||
/**
|
||||
* Loops through the list of whitelist urls
|
||||
* @param url {String} - url the electron is navigated to
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function isWhitelisted(url) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
getGlobalConfigField('whitelistUrl').then((whitelist) => {
|
||||
|
||||
if (checkWhitelist(url, whitelist)) {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
return reject(new Error('URL does not match with the whitelist'));
|
||||
|
||||
}).catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that compares url against a list of whitelist
|
||||
* returns true if hostName or domain present in the whitelist
|
||||
* @param url {String} - url the electron is navigated to
|
||||
* @param whitelist {String} - coma separated whitelists
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function checkWhitelist(url, whitelist) {
|
||||
let whitelistArray = whitelist.split(',');
|
||||
const parsedUrl = parseDomain(url);
|
||||
|
||||
if (!parsedUrl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!whitelist) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!whitelistArray.length || whitelistArray.indexOf('*') !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return whitelistArray.some((whitelistHost) => {
|
||||
let parsedWhitelist = parseDomain(whitelistHost);
|
||||
|
||||
if (!parsedWhitelist) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return matchDomains(parsedUrl, parsedWhitelist);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the respective hostName
|
||||
* @param parsedUrl {Object} - parsed url
|
||||
* @param parsedWhitelist {Object} - parsed whitelist
|
||||
*
|
||||
* example:
|
||||
* matchDomain({ subdomain: www, domain: example, tld: com }, { subdomain: app, domain: example, tld: com })
|
||||
*
|
||||
* @returns {*}
|
||||
*/
|
||||
function matchDomains(parsedUrl, parsedWhitelist) {
|
||||
|
||||
if (isEqual(parsedUrl, parsedWhitelist)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const hostNameFromUrl = parsedUrl.domain + parsedUrl.tld;
|
||||
const hostNameFromWhitelist = parsedWhitelist.domain + parsedWhitelist.tld;
|
||||
|
||||
if (!parsedWhitelist.subdomain) {
|
||||
return hostNameFromUrl === hostNameFromWhitelist
|
||||
}
|
||||
|
||||
return hostNameFromUrl === hostNameFromWhitelist && matchSubDomains(parsedUrl.subdomain, parsedWhitelist.subdomain);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the last occurrence in the sub-domain
|
||||
* @param subDomainUrl {String} - sub-domain from url
|
||||
* @param subDomainWhitelist {String} - sub-domain from whitelist
|
||||
*
|
||||
* example: matchSubDomains('www', 'app')
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function matchSubDomains(subDomainUrl, subDomainWhitelist) {
|
||||
|
||||
if (subDomainUrl === subDomainWhitelist) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const subDomainUrlArray = subDomainUrl.split('.');
|
||||
const lastCharSubDomainUrl = subDomainUrlArray[subDomainUrlArray.length - 1];
|
||||
|
||||
const subDomainWhitelistArray = subDomainWhitelist.split('.');
|
||||
const lastCharWhitelist = subDomainWhitelistArray[subDomainWhitelistArray.length - 1];
|
||||
|
||||
return lastCharSubDomainUrl === lastCharWhitelist;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isWhitelisted,
|
||||
|
||||
// items below here are only exported for testing, do NOT use!
|
||||
checkWhitelist
|
||||
|
||||
};
|
@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const electron = require('electron');
|
||||
const app = electron.app;
|
||||
const crashReporter = electron.crashReporter;
|
||||
@ -21,6 +22,7 @@ const throttle = require('./utils/throttle.js');
|
||||
const { getConfigField, updateConfigField } = require('./config.js');
|
||||
const { isMac, isNodeEnv } = require('./utils/misc');
|
||||
const { deleteIndexFolder } = require('./search/search.js');
|
||||
const { isWhitelisted } = require('./utils/whitelistHandler');
|
||||
|
||||
// show dialog when certificate errors occur
|
||||
require('./dialogs/showCertError.js');
|
||||
@ -37,6 +39,7 @@ let alwaysOnTop = false;
|
||||
let position = 'lower-right';
|
||||
let display;
|
||||
let sandboxed = false;
|
||||
let downloadsDirectory;
|
||||
|
||||
// note: this file is built using browserify in prebuild step.
|
||||
const preloadMainScript = path.join(__dirname, 'preload/_preloadMain.js');
|
||||
@ -128,8 +131,8 @@ function doCreateMainWindow(initialUrl, initialBounds) {
|
||||
newWinOpts.width = bounds.width;
|
||||
newWinOpts.height = bounds.height;
|
||||
} else {
|
||||
newWinOpts.width = 1024;
|
||||
newWinOpts.height = 768;
|
||||
newWinOpts.width = 900;
|
||||
newWinOpts.height = 900;
|
||||
}
|
||||
|
||||
// will center on screen if values not provided
|
||||
@ -238,11 +241,36 @@ function doCreateMainWindow(initialUrl, initialBounds) {
|
||||
|
||||
mainWindow.on('closed', destroyAllWindows);
|
||||
|
||||
// if an user has set a custom downloads directory,
|
||||
// we get that data from the user config file
|
||||
getConfigField('downloadsDirectory')
|
||||
.then((value) => {
|
||||
downloadsDirectory = value;
|
||||
// if the directory has been deleted, try creating it.
|
||||
if (!fs.existsSync(downloadsDirectory)) {
|
||||
const directoryCreated = fs.mkdirSync(downloadsDirectory);
|
||||
// If the directory creation failed, we use the default downloads directory
|
||||
if (!directoryCreated) {
|
||||
downloadsDirectory = null;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
log.send(logLevels.ERROR, 'Could not find the downloads directory config -> ' + error);
|
||||
});
|
||||
|
||||
// Manage File Downloads
|
||||
mainWindow.webContents.session.on('will-download', (event, item, webContents) => {
|
||||
|
||||
// When download is in progress, send necessary data to indicate the same
|
||||
webContents.send('downloadProgress');
|
||||
|
||||
// if the user has set a custom downloads directory, save file to that directory
|
||||
// if otherwise, we save it to the operating system's default downloads directory
|
||||
if (downloadsDirectory) {
|
||||
item.setSavePath(downloadsDirectory + "/" + item.getFilename());
|
||||
}
|
||||
|
||||
// Send file path when download is complete
|
||||
item.once('done', (e, state) => {
|
||||
if (state === 'completed') {
|
||||
@ -257,11 +285,6 @@ function doCreateMainWindow(initialUrl, initialBounds) {
|
||||
});
|
||||
});
|
||||
|
||||
// To delete the user index data folder on navigation
|
||||
mainWindow.webContents.on('will-navigate', () => {
|
||||
deleteIndexFolder();
|
||||
});
|
||||
|
||||
getConfigField('url')
|
||||
.then(initializeCrashReporter)
|
||||
.catch(app.quit);
|
||||
@ -353,6 +376,12 @@ function doCreateMainWindow(initialUrl, initialBounds) {
|
||||
if (browserWin) {
|
||||
log.send(logLevels.INFO, 'loaded pop-out window url: ' + newWinParsedUrl);
|
||||
|
||||
if (!isMac) {
|
||||
// Removes the menu bar from the pop-out window
|
||||
// setMenu is currently only supported on Windows and Linux
|
||||
browserWin.setMenu(null);
|
||||
}
|
||||
|
||||
getConfigField('url')
|
||||
.then((podUrl) => {
|
||||
getConfigField('crashReporter')
|
||||
@ -435,6 +464,21 @@ function doCreateMainWindow(initialUrl, initialBounds) {
|
||||
}
|
||||
});
|
||||
|
||||
// whenever the main window is navigated for ex: window.location.href or url redirect
|
||||
mainWindow.webContents.on('will-navigate', function(event, navigatedURL) {
|
||||
deleteIndexFolder();
|
||||
isWhitelisted(navigatedURL)
|
||||
.catch(() => {
|
||||
event.preventDefault();
|
||||
electron.dialog.showMessageBox(mainWindow, {
|
||||
type: 'warning',
|
||||
buttons: [ 'Ok' ],
|
||||
title: 'Not Allowed',
|
||||
message: `Sorry, you are not allowed to access this website (${navigatedURL}), please contact your administrator for more details`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -590,6 +634,13 @@ function isAlwaysOnTop(boolean) {
|
||||
browserWins.forEach(function (browser) {
|
||||
browser.setAlwaysOnTop(boolean);
|
||||
});
|
||||
|
||||
// An issue where changing the alwaysOnTop property
|
||||
// focus the pop-out window
|
||||
// Issue - Electron-209
|
||||
if (mainWindow && mainWindow.winName) {
|
||||
activate(mainWindow.winName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -598,6 +649,11 @@ eventEmitter.on('isAlwaysOnTop', (boolean) => {
|
||||
isAlwaysOnTop(boolean);
|
||||
});
|
||||
|
||||
// set downloads directory
|
||||
eventEmitter.on('setDownloadsDirectory', (newDirectory) => {
|
||||
downloadsDirectory = newDirectory;
|
||||
});
|
||||
|
||||
// node event emitter for notification settings
|
||||
eventEmitter.on('notificationSettings', (notificationSettings) => {
|
||||
position = notificationSettings.position;
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
library/lz4.exec
BIN
library/lz4.exec
Binary file not shown.
Binary file not shown.
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "Symphony",
|
||||
"productName": "Symphony",
|
||||
"version": "2.0.0",
|
||||
"buildNumber": "",
|
||||
"version": "2.2.0",
|
||||
"buildNumber": "124",
|
||||
"description": "Symphony desktop app (Foundation ODP)",
|
||||
"author": "Symphony",
|
||||
"main": "js/main.js",
|
||||
@ -112,9 +112,11 @@
|
||||
"ffi": "^2.2.0",
|
||||
"filesize": "^3.5.10",
|
||||
"keymirror": "0.1.1",
|
||||
"lodash.difference": "^4.5.0",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"lodash.omit": "^4.5.0",
|
||||
"lodash.pick": "^4.4.0",
|
||||
"lodash.difference": "^4.5.0",
|
||||
"parse-domain": "^2.0.0",
|
||||
"randomstring": "^1.1.5",
|
||||
"ref": "^1.3.4",
|
||||
"shell-path": "^2.1.0",
|
||||
|
74
tests/utils/whitelist.test.js
Normal file
74
tests/utils/whitelist.test.js
Normal file
@ -0,0 +1,74 @@
|
||||
const { checkWhitelist } = require('../../js/utils/whitelistHandler');
|
||||
|
||||
describe('validate url with whitelist', function() {
|
||||
|
||||
describe('checkWhitelist truth tests', function() {
|
||||
|
||||
it('should return true when the url is valid', function() {
|
||||
const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com';
|
||||
const url = 'https://my.symphony.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return true when if hostName is defined', function() {
|
||||
const whitelist = 'www.symphony.com, app.symphony.com, symphony.com';
|
||||
const url = 'https://xyz.symphony.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return true when the first occurrence of sub-domain is matched', function() {
|
||||
const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com';
|
||||
const url = 'https://xyz.my.symphony.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return true when for any URL if whitelist has *', function() {
|
||||
const whitelist = '*';
|
||||
const url = 'https://www.example.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('checkWhitelist falsity tests', function () {
|
||||
|
||||
it('should return false when sub-domain does not match', function () {
|
||||
const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com';
|
||||
const url = 'https://xyz.symphony.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return false when hostName does not match', function () {
|
||||
const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com';
|
||||
const url = 'https://my.example.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return false when the URL is invalid', function () {
|
||||
const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com';
|
||||
const url = 'invalidUrl';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return false when the whitelist is invalid', function () {
|
||||
const whitelist = 'invalidWhitelist';
|
||||
const url = 'https://www.symphony.com';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return false if whitelist is empty', function() {
|
||||
const whitelist = '';
|
||||
const url = 'https://www.example.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user