[MM-14740] Integrate GPO functionality (#961)
* integrate gpo functionality * support multiple windows registry ‘hives’ * correct some copy paste errors * registry config progress * tweaks
This commit is contained in:
@@ -80,6 +80,7 @@
|
|||||||
"src/common/config/buildConfig.js",
|
"src/common/config/buildConfig.js",
|
||||||
"src/common/config/pastDefaultPreferences.js",
|
"src/common/config/pastDefaultPreferences.js",
|
||||||
"src/common/config/upgradePreferences.js",
|
"src/common/config/upgradePreferences.js",
|
||||||
|
"src/common/config/RegistryConfig.js",
|
||||||
"src/common/osVersion.js",
|
"src/common/osVersion.js",
|
||||||
"src/common/config/defaultPreferences.js",
|
"src/common/config/defaultPreferences.js",
|
||||||
"src/common/JsonFileManager.js",
|
"src/common/JsonFileManager.js",
|
||||||
|
@@ -19,7 +19,7 @@ import AutoSaveIndicator from './AutoSaveIndicator.jsx';
|
|||||||
const CONFIG_TYPE_SERVERS = 'servers';
|
const CONFIG_TYPE_SERVERS = 'servers';
|
||||||
const CONFIG_TYPE_APP_OPTIONS = 'appOptions';
|
const CONFIG_TYPE_APP_OPTIONS = 'appOptions';
|
||||||
|
|
||||||
const config = new Config(remote.app.getPath('userData') + '/config.json');
|
const config = new Config(remote.app.getPath('userData') + '/config.json', remote.getCurrentWindow().registryConfigData);
|
||||||
|
|
||||||
function backToIndex(index) {
|
function backToIndex(index) {
|
||||||
const target = typeof index === 'undefined' ? 0 : index;
|
const target = typeof index === 'undefined' ? 0 : index;
|
||||||
@@ -340,7 +340,7 @@ export default class SettingsPage extends React.Component {
|
|||||||
addServer={this.addServer}
|
addServer={this.addServer}
|
||||||
allowTeamEdit={this.state.enableTeamModification}
|
allowTeamEdit={this.state.enableTeamModification}
|
||||||
onTeamClick={(index) => {
|
onTeamClick={(index) => {
|
||||||
backToIndex(index + this.state.buildTeams.length + this.state.GPOTeams.length);
|
backToIndex(index + this.state.buildTeams.length + this.state.registryTeams.length);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -618,7 +618,7 @@ export default class SettingsPage extends React.Component {
|
|||||||
bsStyle='link'
|
bsStyle='link'
|
||||||
style={settingsPage.close}
|
style={settingsPage.close}
|
||||||
onClick={this.handleCancel}
|
onClick={this.handleCancel}
|
||||||
disabled={this.state.localTeams.length === 0}
|
disabled={this.state.teams.length === 0}
|
||||||
>
|
>
|
||||||
<span>{'×'}</span>
|
<span>{'×'}</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
@@ -21,7 +21,7 @@ import Config from '../common/config';
|
|||||||
import MainPage from './components/MainPage.jsx';
|
import MainPage from './components/MainPage.jsx';
|
||||||
import {createDataURL as createBadgeDataURL} from './js/badge';
|
import {createDataURL as createBadgeDataURL} from './js/badge';
|
||||||
|
|
||||||
const config = new Config(remote.app.getPath('userData') + '/config.json');
|
const config = new Config(remote.app.getPath('userData') + '/config.json', remote.getCurrentWindow().registryConfigData);
|
||||||
|
|
||||||
const teams = config.teams;
|
const teams = config.teams;
|
||||||
|
|
||||||
|
106
src/common/config/RegistryConfig.js
Normal file
106
src/common/config/RegistryConfig.js
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
// Copyright (c) 2015-2016 Yuya Ochiai
|
||||||
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import {EventEmitter} from 'events';
|
||||||
|
|
||||||
|
import WindowsRegistry from 'winreg';
|
||||||
|
|
||||||
|
const REGISTRY_HIVE_LIST = [WindowsRegistry.HKLM, WindowsRegistry.HKCU];
|
||||||
|
const BASE_REGISTRY_KEY_PATH = '\\Software\\Policies\\Mattermost';
|
||||||
|
|
||||||
|
export default class RegistryConfig extends EventEmitter {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.initialized = false;
|
||||||
|
this.data = {
|
||||||
|
teams: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async init() {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
// extract DefaultServerList from the registry
|
||||||
|
try {
|
||||||
|
const servers = await this.getServersListFromRegistry();
|
||||||
|
if (servers.length) {
|
||||||
|
this.data.teams.push(...servers);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('[RegistryConfig] Nothing retrieved for \'DefaultServerList\'', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract EnableServerManagement from the registry
|
||||||
|
try {
|
||||||
|
const enableServerManagement = await this.getEnableServerManagementFromRegistry();
|
||||||
|
if (enableServerManagement !== null) {
|
||||||
|
this.data.enableServerManagement = enableServerManagement;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('[RegistryConfig] Nothing retrieved for \'EnableServerManagement\'', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract EnableAutoUpdater from the registry
|
||||||
|
try {
|
||||||
|
const enableAutoUpdater = await this.getEnableAutoUpdatorFromRegistry();
|
||||||
|
if (enableAutoUpdater !== null) {
|
||||||
|
this.data.enableAutoUpdater = enableAutoUpdater;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('[RegistryConfig] Nothing retrieved for \'EnableAutoUpdater\'', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initialized = true;
|
||||||
|
this.emit('update', this.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getServersListFromRegistry() {
|
||||||
|
const defaultTeams = await this.getRegistryEntry(`${BASE_REGISTRY_KEY_PATH}\\DefaultServerList`);
|
||||||
|
return defaultTeams.flat(2).reduce((teams, team) => {
|
||||||
|
if (team) {
|
||||||
|
teams.push({
|
||||||
|
name: team.name,
|
||||||
|
url: team.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return teams;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEnableServerManagementFromRegistry() {
|
||||||
|
const entries = (await this.getRegistryEntry(BASE_REGISTRY_KEY_PATH, 'EnableServerManagement'));
|
||||||
|
const entry = entries.pop();
|
||||||
|
return entry ? entry === '0x1' : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEnableAutoUpdatorFromRegistry() {
|
||||||
|
const entries = (await this.getRegistryEntry(BASE_REGISTRY_KEY_PATH, 'EnableAutoUpdater'));
|
||||||
|
const entry = entries.pop();
|
||||||
|
return entry ? entry === '0x1' : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRegistryEntry(key, name) {
|
||||||
|
const results = [];
|
||||||
|
for (const hive of REGISTRY_HIVE_LIST) {
|
||||||
|
results.push(this.getRegistryEntryValues(new WindowsRegistry({hive, key}), name));
|
||||||
|
}
|
||||||
|
const entryValues = await Promise.all(results);
|
||||||
|
return entryValues.filter((value) => value);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRegistryEntryValues(regKey, name) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
regKey.values((error, items) => {
|
||||||
|
if (error || !items || !items.length) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (name) { // looking for a single entry value
|
||||||
|
const registryItem = items.find((item) => item.name === name);
|
||||||
|
resolve(registryItem && registryItem.value ? registryItem.value : null);
|
||||||
|
} else { // looking for an entry list
|
||||||
|
resolve(items);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -15,9 +15,10 @@ import buildConfig from './buildConfig';
|
|||||||
* Handles loading and merging all sources of configuration as well as saving user provided config
|
* Handles loading and merging all sources of configuration as well as saving user provided config
|
||||||
*/
|
*/
|
||||||
export default class Config extends EventEmitter {
|
export default class Config extends EventEmitter {
|
||||||
constructor(configFilePath) {
|
constructor(configFilePath, registryConfigData = {teams: []}) {
|
||||||
super();
|
super();
|
||||||
this.configFilePath = configFilePath;
|
this.configFilePath = configFilePath;
|
||||||
|
this.registryConfigData = registryConfigData;
|
||||||
this.reload();
|
this.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,8 +36,6 @@ export default class Config extends EventEmitter {
|
|||||||
this.localConfigData = this.loadLocalConfigFile();
|
this.localConfigData = this.loadLocalConfigFile();
|
||||||
this.localConfigData = this.checkForConfigUpdates(this.localConfigData);
|
this.localConfigData = this.checkForConfigUpdates(this.localConfigData);
|
||||||
|
|
||||||
this.GPOConfigData = this.loadGPOConfigData();
|
|
||||||
|
|
||||||
this.regenerateCombinedConfigData();
|
this.regenerateCombinedConfigData();
|
||||||
|
|
||||||
this.emit('update', this.combinedData);
|
this.emit('update', this.combinedData);
|
||||||
@@ -77,6 +76,11 @@ export default class Config extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setRegistryConfigData(registryConfigData = {teams: []}) {
|
||||||
|
this.registryConfigData = Object.assign({}, registryConfigData);
|
||||||
|
this.reload();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to replace the existing config data with new config data
|
* Used to replace the existing config data with new config data
|
||||||
*
|
*
|
||||||
@@ -126,8 +130,8 @@ export default class Config extends EventEmitter {
|
|||||||
get buildData() {
|
get buildData() {
|
||||||
return this.buildConfigData;
|
return this.buildConfigData;
|
||||||
}
|
}
|
||||||
get GPOData() {
|
get registryData() {
|
||||||
return this.GPOConfigData;
|
return this.registryConfigData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// convenience getters
|
// convenience getters
|
||||||
@@ -142,7 +146,7 @@ export default class Config extends EventEmitter {
|
|||||||
return this.localConfigData.teams;
|
return this.localConfigData.teams;
|
||||||
}
|
}
|
||||||
get predefinedTeams() {
|
get predefinedTeams() {
|
||||||
return [...this.buildConfigData.defaultTeams, ...this.GPOConfigData.teams];
|
return [...this.buildConfigData.defaultTeams, ...this.registryConfigData.teams];
|
||||||
}
|
}
|
||||||
get enableHardwareAcceleration() {
|
get enableHardwareAcceleration() {
|
||||||
return this.combinedData.enableHardwareAcceleration;
|
return this.combinedData.enableHardwareAcceleration;
|
||||||
@@ -216,23 +220,6 @@ export default class Config extends EventEmitter {
|
|||||||
return configData;
|
return configData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads and returns config data defined in GPO for Windows
|
|
||||||
*/
|
|
||||||
loadGPOConfigData() {
|
|
||||||
const configData = {
|
|
||||||
teams: [],
|
|
||||||
enableServerManagement: true,
|
|
||||||
enableAutoUpdater: true,
|
|
||||||
};
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
//
|
|
||||||
// TODO: GPO data needs to be retrieved here and merged into the local `configData` variable for return
|
|
||||||
//
|
|
||||||
}
|
|
||||||
return configData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if locally stored data needs to be updated and upgrades as needed
|
* Determines if locally stored data needs to be updated and upgrades as needed
|
||||||
*
|
*
|
||||||
@@ -257,7 +244,7 @@ export default class Config extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
regenerateCombinedConfigData() {
|
regenerateCombinedConfigData() {
|
||||||
// combine all config data in the correct order
|
// combine all config data in the correct order
|
||||||
this.combinedData = Object.assign({}, this.defaultConfigData, this.localConfigData, this.buildConfigData, this.GPOConfigData);
|
this.combinedData = Object.assign({}, this.defaultConfigData, this.localConfigData, this.buildConfigData, this.registryConfigData);
|
||||||
|
|
||||||
// remove unecessary data pulled from default and build config
|
// remove unecessary data pulled from default and build config
|
||||||
delete this.combinedData.defaultTeam;
|
delete this.combinedData.defaultTeam;
|
||||||
@@ -271,9 +258,9 @@ export default class Config extends EventEmitter {
|
|||||||
combinedTeams.push(...this.buildConfigData.defaultTeams);
|
combinedTeams.push(...this.buildConfigData.defaultTeams);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - add GPO defined teams, if any
|
// - add registry defined teams, if any
|
||||||
if (this.GPOConfigData.teams && this.GPOConfigData.teams.length) {
|
if (this.registryConfigData.teams && this.registryConfigData.teams.length) {
|
||||||
combinedTeams.push(...this.GPOConfigData.teams);
|
combinedTeams.push(...this.registryConfigData.teams);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - add locally defined teams only if server management is enabled
|
// - add locally defined teams only if server management is enabled
|
||||||
@@ -284,7 +271,7 @@ export default class Config extends EventEmitter {
|
|||||||
this.combinedData.teams = combinedTeams;
|
this.combinedData.teams = combinedTeams;
|
||||||
this.combinedData.localTeams = this.localConfigData.teams;
|
this.combinedData.localTeams = this.localConfigData.teams;
|
||||||
this.combinedData.buildTeams = this.buildConfigData.defaultTeams;
|
this.combinedData.buildTeams = this.buildConfigData.defaultTeams;
|
||||||
this.combinedData.GPOTeams = this.GPOConfigData.teams;
|
this.combinedData.registryTeams = this.registryConfigData.teams;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
16
src/main.js
16
src/main.js
@@ -37,6 +37,7 @@ global.willAppQuit = false;
|
|||||||
|
|
||||||
app.setAppUserModelId('com.squirrel.mattermost.Mattermost'); // Use explicit AppUserModelID
|
app.setAppUserModelId('com.squirrel.mattermost.Mattermost'); // Use explicit AppUserModelID
|
||||||
|
|
||||||
|
import RegistryConfig from './common/config/RegistryConfig';
|
||||||
import Config from './common/config';
|
import Config from './common/config';
|
||||||
import CertificateStore from './main/certificateStore';
|
import CertificateStore from './main/certificateStore';
|
||||||
const certificateStore = CertificateStore.load(path.resolve(app.getPath('userData'), 'certificate.json'));
|
const certificateStore = CertificateStore.load(path.resolve(app.getPath('userData'), 'certificate.json'));
|
||||||
@@ -63,7 +64,9 @@ let deeplinkingUrl = null;
|
|||||||
let scheme = null;
|
let scheme = null;
|
||||||
let appState = null;
|
let appState = null;
|
||||||
let permissionManager = null;
|
let permissionManager = null;
|
||||||
let config = null;
|
|
||||||
|
const registryConfig = new RegistryConfig();
|
||||||
|
const config = new Config(app.getPath('userData') + '/config.json');
|
||||||
|
|
||||||
const argv = parseArgv(process.argv.slice(1));
|
const argv = parseArgv(process.argv.slice(1));
|
||||||
const hideOnStartup = shouldBeHiddenOnStartup(argv);
|
const hideOnStartup = shouldBeHiddenOnStartup(argv);
|
||||||
@@ -74,13 +77,20 @@ if (argv['data-dir']) {
|
|||||||
|
|
||||||
global.isDev = isDev && !argv.disableDevMode;
|
global.isDev = isDev && !argv.disableDevMode;
|
||||||
|
|
||||||
config = new Config(app.getPath('userData') + '/config.json');
|
|
||||||
|
|
||||||
// can only call this before the app is ready
|
// can only call this before the app is ready
|
||||||
if (config.enableHardwareAcceleration === false) {
|
if (config.enableHardwareAcceleration === false) {
|
||||||
app.disableHardwareAcceleration();
|
app.disableHardwareAcceleration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registryConfig.on('update', (registryConfigData) => {
|
||||||
|
config.setRegistryConfigData(registryConfigData);
|
||||||
|
if (app.isReady() && mainWindow) {
|
||||||
|
mainWindow.registryConfigData = registryConfigData;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
registryConfig.init();
|
||||||
|
|
||||||
config.on('update', (configData) => {
|
config.on('update', (configData) => {
|
||||||
if (process.platform === 'win32' || process.platform === 'linux') {
|
if (process.platform === 'win32' || process.platform === 'linux') {
|
||||||
const appLauncher = new AutoLauncher();
|
const appLauncher = new AutoLauncher();
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
"semver": "^5.5.0",
|
"semver": "^5.5.0",
|
||||||
"simple-spellchecker": "^0.9.8",
|
"simple-spellchecker": "^0.9.8",
|
||||||
"underscore": "^1.9.1",
|
"underscore": "^1.9.1",
|
||||||
|
"winreg": "^1.2.4",
|
||||||
"yargs": "^3.32.0"
|
"yargs": "^3.32.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user