[MM-56389] Switch to registry-js
native module for Windows registry reading (#3460)
This commit is contained in:
@@ -3,54 +3,55 @@
|
||||
|
||||
import RegistryConfig from 'common/config/RegistryConfig';
|
||||
|
||||
jest.mock('winreg', () => {
|
||||
return jest.fn().mockImplementation(({hive, key}) => {
|
||||
return {
|
||||
values: (fn) => {
|
||||
if (hive === 'correct-hive') {
|
||||
fn(null, [
|
||||
{
|
||||
name: `${key}-name-1`,
|
||||
value: `${key}-value-1`,
|
||||
jest.mock('registry-js', () => {
|
||||
return {
|
||||
HKEY: {
|
||||
HKEY_LOCAL_MACHINE: 'HKEY_LOCAL_MACHINE',
|
||||
HKEY_CURRENT_USER: 'HKEY_CURRENT_USER',
|
||||
},
|
||||
enumerateValues: jest.fn().mockImplementation((hive, key) => {
|
||||
if (hive === 'correct-hive') {
|
||||
return [
|
||||
{
|
||||
name: `${key}-name-1`,
|
||||
data: `${key}-value-1`,
|
||||
|
||||
},
|
||||
},
|
||||
{
|
||||
name: `${key}-name-2`,
|
||||
data: `${key}-value-2`,
|
||||
},
|
||||
];
|
||||
} else if (hive === 'mattermost-hive') {
|
||||
if (key.endsWith('DefaultServerList')) {
|
||||
return [
|
||||
{
|
||||
name: `${key}-name-2`,
|
||||
value: `${key}-value-2`,
|
||||
name: 'server-1',
|
||||
data: 'http://server-1.com',
|
||||
},
|
||||
]);
|
||||
} else if (hive === 'mattermost-hive') {
|
||||
if (key.endsWith('DefaultServerList')) {
|
||||
fn(null, [
|
||||
{
|
||||
name: 'server-1',
|
||||
value: 'http://server-1.com',
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
fn(null, [
|
||||
{
|
||||
name: 'EnableServerManagement',
|
||||
value: '0x1',
|
||||
},
|
||||
{
|
||||
name: 'EnableAutoUpdater',
|
||||
value: '0x1',
|
||||
},
|
||||
]);
|
||||
}
|
||||
} else if (hive === 'really-bad-hive') {
|
||||
throw new Error('This is an error');
|
||||
} else {
|
||||
fn('Error', []);
|
||||
];
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
return [
|
||||
{
|
||||
name: 'EnableServerManagement',
|
||||
data: '0x1',
|
||||
},
|
||||
{
|
||||
name: 'EnableAutoUpdater',
|
||||
data: '0x1',
|
||||
},
|
||||
];
|
||||
} else if (hive === 'really-bad-hive') {
|
||||
throw new Error('This is an error');
|
||||
}
|
||||
|
||||
return [];
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('common/config/RegistryConfig', () => {
|
||||
it('should initialize correctly', async () => {
|
||||
it('should initialize correctly', () => {
|
||||
const originalPlatform = process.platform;
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: 'win32',
|
||||
@@ -59,7 +60,7 @@ describe('common/config/RegistryConfig', () => {
|
||||
const registryConfig = new RegistryConfig();
|
||||
const originalFn = registryConfig.getRegistryEntryValues;
|
||||
registryConfig.getRegistryEntryValues = (hive, key, name) => originalFn.apply(registryConfig, ['mattermost-hive', key, name, false]);
|
||||
await registryConfig.init();
|
||||
registryConfig.init();
|
||||
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: originalPlatform,
|
||||
@@ -76,32 +77,32 @@ describe('common/config/RegistryConfig', () => {
|
||||
const registryConfig = new RegistryConfig();
|
||||
|
||||
it('should return correct values', () => {
|
||||
expect(registryConfig.getRegistryEntryValues('correct-hive', 'correct-key', null, false)).resolves.toStrictEqual([
|
||||
expect(registryConfig.getRegistryEntryValues('correct-hive', 'correct-key', null, false)).toStrictEqual([
|
||||
{
|
||||
name: 'correct-key-name-1',
|
||||
value: 'correct-key-value-1',
|
||||
data: 'correct-key-value-1',
|
||||
},
|
||||
{
|
||||
name: 'correct-key-name-2',
|
||||
value: 'correct-key-value-2',
|
||||
data: 'correct-key-value-2',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return correct value by name', () => {
|
||||
expect(registryConfig.getRegistryEntryValues('correct-hive', 'correct-key', 'correct-key-name-1', false)).resolves.toBe('correct-key-value-1');
|
||||
expect(registryConfig.getRegistryEntryValues('correct-hive', 'correct-key', 'correct-key-name-1', false)).toBe('correct-key-value-1');
|
||||
});
|
||||
|
||||
it('should return undefined with wrong name', () => {
|
||||
expect(registryConfig.getRegistryEntryValues('correct-hive', 'correct-key', 'wrong-key-name-1', false)).resolves.toBe(undefined);
|
||||
expect(registryConfig.getRegistryEntryValues('correct-hive', 'correct-key', 'wrong-key-name-1', false)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should return undefined with bad hive', () => {
|
||||
expect(registryConfig.getRegistryEntryValues('bad-hive', 'correct-key', null, false)).resolves.toBe(undefined);
|
||||
expect(registryConfig.getRegistryEntryValues('bad-hive', 'correct-key', null, false)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should call reject when an error occurs', () => {
|
||||
expect(registryConfig.getRegistryEntryValues('really-bad-hive', 'correct-key', null, false)).rejects.toThrow(new Error('This is an error'));
|
||||
it('should return undefined when an error occurs', () => {
|
||||
expect(registryConfig.getRegistryEntryValues('really-bad-hive', 'correct-key', null, false)).toBe(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -3,16 +3,16 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {EventEmitter} from 'events';
|
||||
import WindowsRegistry from 'winreg';
|
||||
import WindowsRegistryUTF8 from 'winreg-utf8';
|
||||
import type {RegistryValue} from 'registry-js';
|
||||
import {HKEY, enumerateValues} from 'registry-js';
|
||||
|
||||
import {Logger} from 'common/log';
|
||||
|
||||
import type {RegistryConfig as RegistryConfigType, Server} from 'types/config';
|
||||
|
||||
const log = new Logger('RegistryConfig');
|
||||
const REGISTRY_HIVE_LIST = [WindowsRegistry.HKLM, WindowsRegistry.HKCU];
|
||||
const BASE_REGISTRY_KEY_PATH = '\\Software\\Policies\\Mattermost';
|
||||
const REGISTRY_HIVE_LIST = [HKEY.HKEY_LOCAL_MACHINE, HKEY.HKEY_CURRENT_USER];
|
||||
const BASE_REGISTRY_KEY_PATH = 'SOFTWARE\\Policies\\Mattermost';
|
||||
export const REGISTRY_READ_EVENT = 'registry-read';
|
||||
|
||||
/**
|
||||
@@ -31,15 +31,15 @@ export default class RegistryConfig extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers loading data from Windows registry, supports async/await
|
||||
* Triggers loading data from Windows registry
|
||||
*
|
||||
* @emits {update} emitted once all data has been loaded from the registry
|
||||
*/
|
||||
async init() {
|
||||
init() {
|
||||
if (process.platform === 'win32') {
|
||||
// extract DefaultServerList from the registry
|
||||
try {
|
||||
const servers = await this.getServersListFromRegistry();
|
||||
const servers = this.getServersListFromRegistry();
|
||||
if (servers.length) {
|
||||
this.data.servers!.push(...servers);
|
||||
}
|
||||
@@ -49,7 +49,7 @@ export default class RegistryConfig extends EventEmitter {
|
||||
|
||||
// extract EnableServerManagement from the registry
|
||||
try {
|
||||
const enableServerManagement = await this.getEnableServerManagementFromRegistry();
|
||||
const enableServerManagement = this.getEnableServerManagementFromRegistry();
|
||||
if (enableServerManagement !== null) {
|
||||
this.data.enableServerManagement = enableServerManagement;
|
||||
}
|
||||
@@ -59,7 +59,7 @@ export default class RegistryConfig extends EventEmitter {
|
||||
|
||||
// extract EnableAutoUpdater from the registry
|
||||
try {
|
||||
const enableAutoUpdater = await this.getEnableAutoUpdatorFromRegistry();
|
||||
const enableAutoUpdater = this.getEnableAutoUpdatorFromRegistry();
|
||||
if (enableAutoUpdater !== null) {
|
||||
this.data.enableAutoUpdater = enableAutoUpdater;
|
||||
}
|
||||
@@ -76,13 +76,13 @@ export default class RegistryConfig extends EventEmitter {
|
||||
/**
|
||||
* Extracts a list of servers
|
||||
*/
|
||||
async getServersListFromRegistry() {
|
||||
const defaultServers = await this.getRegistryEntry(`${BASE_REGISTRY_KEY_PATH}\\DefaultServerList`);
|
||||
private getServersListFromRegistry() {
|
||||
const defaultServers = this.getRegistryEntry(`${BASE_REGISTRY_KEY_PATH}\\DefaultServerList`);
|
||||
return defaultServers.flat(2).reduce((servers: Server[], server) => {
|
||||
if (server) {
|
||||
servers.push({
|
||||
name: (server as WindowsRegistry.RegistryItem).name,
|
||||
url: (server as WindowsRegistry.RegistryItem).value,
|
||||
name: (server as RegistryValue).name,
|
||||
url: (server as RegistryValue).data as string,
|
||||
});
|
||||
}
|
||||
return servers;
|
||||
@@ -92,8 +92,8 @@ export default class RegistryConfig extends EventEmitter {
|
||||
/**
|
||||
* Determines whether server management has been enabled, disabled or isn't configured
|
||||
*/
|
||||
async getEnableServerManagementFromRegistry() {
|
||||
const entries = (await this.getRegistryEntry(BASE_REGISTRY_KEY_PATH, 'EnableServerManagement'));
|
||||
private getEnableServerManagementFromRegistry() {
|
||||
const entries = this.getRegistryEntry(BASE_REGISTRY_KEY_PATH, 'EnableServerManagement');
|
||||
const entry = entries.pop();
|
||||
return entry ? entry === '0x1' : null;
|
||||
}
|
||||
@@ -101,8 +101,8 @@ export default class RegistryConfig extends EventEmitter {
|
||||
/**
|
||||
* Determines whether the auto updated has been enabled, disabled or isn't configured
|
||||
*/
|
||||
async getEnableAutoUpdatorFromRegistry() {
|
||||
const entries = (await this.getRegistryEntry(BASE_REGISTRY_KEY_PATH, 'EnableAutoUpdater'));
|
||||
private getEnableAutoUpdatorFromRegistry() {
|
||||
const entries = this.getRegistryEntry(BASE_REGISTRY_KEY_PATH, 'EnableAutoUpdater');
|
||||
const entry = entries.pop();
|
||||
return entry ? entry === '0x1' : null;
|
||||
}
|
||||
@@ -113,13 +113,12 @@ export default class RegistryConfig extends EventEmitter {
|
||||
* @param {string} key Path to the registry key to return
|
||||
* @param {string} name Name of specific entry in the registry key to retrieve (optional)
|
||||
*/
|
||||
async getRegistryEntry(key: string, name?: string) {
|
||||
private getRegistryEntry(key: string, name?: string) {
|
||||
const results = [];
|
||||
for (const hive of REGISTRY_HIVE_LIST) {
|
||||
results.push(this.getRegistryEntryValues(hive, key, name));
|
||||
}
|
||||
const entryValues = await Promise.all(results);
|
||||
return entryValues.filter((value) => value);
|
||||
return results.filter((value) => value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,54 +127,22 @@ export default class RegistryConfig extends EventEmitter {
|
||||
* @param {WindowsRegistry} regKey A configured instance of the WindowsRegistry class
|
||||
* @param {string} name Name of the specific entry to retrieve (optional)
|
||||
*/
|
||||
getRegistryEntryValues(hive: string, key: string, name?: string, utf8 = true) {
|
||||
return new Promise<string | WindowsRegistry.RegistryItem[] | undefined>((resolve, reject) => {
|
||||
try {
|
||||
const registry = this.createRegistry(hive, key, utf8);
|
||||
registry.values((error: Error, results: WindowsRegistry.RegistryItem[]) => {
|
||||
if (error) {
|
||||
this.handleRegistryEntryError(error, hive, key, name, utf8).then((result) => {
|
||||
resolve(result);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!results || results.length === 0) {
|
||||
resolve(undefined);
|
||||
return;
|
||||
}
|
||||
if (name) { // looking for a single entry value
|
||||
const registryItem = results.find((item) => item.name === name);
|
||||
resolve(registryItem && registryItem.value ? registryItem.value : undefined);
|
||||
} else { // looking for an entry list
|
||||
resolve(results);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
this.handleRegistryEntryError(e as Error, hive, key, name, utf8).then((result) => {
|
||||
if (result) {
|
||||
resolve(result);
|
||||
}
|
||||
reject(e);
|
||||
});
|
||||
private getRegistryEntryValues(hive: HKEY, key: string, name?: string) {
|
||||
try {
|
||||
const results = enumerateValues(hive, key);
|
||||
if (!results || results.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
if (name) { // looking for a single entry value
|
||||
const registryItem = results.find((item) => item.name === name);
|
||||
return registryItem && registryItem.data ? registryItem.data : undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleRegistryEntryError(e: Error, hive: string, key: string, name?: string, utf8?: boolean) {
|
||||
log.debug('There was an error accessing the registry for', {hive, key, name, utf8}, e);
|
||||
if (utf8) {
|
||||
log.debug('Trying without UTF-8...', {hive, key, name});
|
||||
return this.getRegistryEntryValues(hive, key, name, false);
|
||||
// looking for an entry list
|
||||
return results;
|
||||
} catch (e) {
|
||||
log.debug('There was an error accessing the registry for', {hive, key, name}, e);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
createRegistry(hive: string, key: string, utf8 = true) {
|
||||
if (utf8) {
|
||||
return new WindowsRegistryUTF8({hive, key, utf8});
|
||||
}
|
||||
|
||||
return new WindowsRegistry({hive, key});
|
||||
}
|
||||
}
|
||||
|
@@ -250,7 +250,6 @@ function initializeBeforeAppReady() {
|
||||
|
||||
if (process.platform === 'darwin' || process.platform === 'win32') {
|
||||
nativeTheme.on('updated', handleUpdateTheme);
|
||||
handleUpdateTheme();
|
||||
}
|
||||
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
@@ -389,6 +388,7 @@ async function initializeAfterAppReady() {
|
||||
catch((err) => log.error('An error occurred: ', err));
|
||||
}
|
||||
|
||||
handleUpdateTheme();
|
||||
MainWindow.show();
|
||||
|
||||
let deeplinkingURL;
|
||||
|
34
src/types/external/winreg-utf8.d.ts
vendored
34
src/types/external/winreg-utf8.d.ts
vendored
@@ -1,34 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
declare module 'winreg-utf8' {
|
||||
import WindowsRegistry from 'winreg';
|
||||
export = WindowsRegistry;
|
||||
}
|
||||
|
||||
declare namespace Winreg {
|
||||
export interface Options {
|
||||
|
||||
/**
|
||||
* Optional hostname, must start with '\\' sequence.
|
||||
*/
|
||||
host?: string;
|
||||
|
||||
/**
|
||||
* Optional hive ID, default is HKLM.
|
||||
*/
|
||||
hive?: string;
|
||||
|
||||
/**
|
||||
* Optional key, default is the root key.
|
||||
*/
|
||||
key?: string;
|
||||
|
||||
/**
|
||||
* Optional registry hive architecture ('x86' or 'x64'; only valid on Windows 64 Bit Operating Systems).
|
||||
*/
|
||||
arch?: string;
|
||||
|
||||
utf8?: boolean;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user