SDA-3565 - Allow C2 / extensions to determine the presence of Citrix media redirection (#1331)

* Initial implementation
This commit is contained in:
Robin Westberg 2022-01-31 10:05:31 +01:00 committed by GitHub
parent 76c4d4de7d
commit 179e953497
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 182 additions and 0 deletions

9
package-lock.json generated
View File

@ -14489,6 +14489,15 @@
"rc": "^1.2.8"
}
},
"registry-js": {
"version": "1.15.1",
"resolved": "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/registry-js/-/registry-js-1.15.1.tgz",
"integrity": "sha1-9Bwym/wkYbbCDhrqPwsuJrbWvdU=",
"requires": {
"node-addon-api": "^3.1.0",
"prebuild-install": "^5.3.5"
}
},
"registry-url": {
"version": "5.1.0",
"resolved": "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/registry-url/-/registry-url-5.1.0.tgz",

View File

@ -171,6 +171,7 @@
"react": "16.13.0",
"react-dom": "16.13.0",
"ref-napi": "1.4.3",
"registry-js": "^1.15.1",
"rimraf": "^3.0.2",
"save-svg-as-png": "^1.4.17",
"shell-path": "2.1.0"

View File

@ -0,0 +1,72 @@
import { enumerateValues } from 'registry-js';
import { getCitrixMediaRedirectionStatus, RedirectionStatus } from '../src/app/citrix-handler';
jest.mock('registry-js');
jest.mock('../src/common/env', () => {
return {
isWindowsOS: true,
isLinux: false,
isMac: false,
};
});
describe('citrix handler', () => {
const mockEnumerateValues = (enumerateValues as unknown) as jest.MockInstance<
typeof enumerateValues
>;
beforeEach(() => {
jest.clearAllMocks().resetModules();
});
it('status inactive', () => {
mockEnumerateValues.mockReturnValue([
{
name: 'OtherValue',
type: 'REG_SZ',
data: 42,
},
]);
const status = getCitrixMediaRedirectionStatus();
expect(status).toBe(RedirectionStatus.INACTIVE);
});
it('status supported', () => {
mockEnumerateValues.mockReturnValue([
{
name: 'MSTeamsRedirectionSupport',
type: 'REG_DWORD',
data: 1,
},
]);
const status = getCitrixMediaRedirectionStatus();
expect(status).toBe(RedirectionStatus.SUPPORTED);
});
it('status unsupported', () => {
mockEnumerateValues.mockReturnValue([
{
name: 'MSTeamsRedirectionSupport',
type: 'REG_DWORD',
data: 0,
},
]);
const status = getCitrixMediaRedirectionStatus();
expect(status).toBe(RedirectionStatus.UNSUPPORTED);
});
it('non-windows os', () => {
jest.mock('../src/common/env', () => {
return {
isWindowsOS: false,
isLinux: true,
isMac: false,
};
});
const { getCitrixMediaRedirectionStatus, RedirectionStatus } = require('../src/app/citrix-handler');
const status = getCitrixMediaRedirectionStatus();
expect(status).toBe(RedirectionStatus.INACTIVE);
expect(mockEnumerateValues).not.toHaveBeenCalled();
});
});

View File

@ -1,4 +1,5 @@
import { activityDetection } from '../src/app/activity-detection';
import { getCitrixMediaRedirectionStatus } from '../src/app/citrix-handler';
import { downloadHandler } from '../src/app/download-handler';
import '../src/app/main-api-handler';
import { protocolHandler } from '../src/app/protocol-handler';
@ -128,6 +129,12 @@ jest.mock('../src/app/notifications/notification-helper', () => {
};
});
jest.mock('../src/app/citrix-handler', () => {
return {
getCitrixMediaRedirectionStatus: jest.fn(),
};
});
describe('main api handler', () => {
beforeEach(() => {
jest.clearAllMocks();
@ -483,5 +490,13 @@ describe('main api handler', () => {
ipcMain.send(apiName.symphonyApi, value);
expect(fromWebContentsMocked.getNativeWindowHandle).toBeCalledTimes(1);
});
it('should call `getCitrixMediaRedirectionStatus` correctly', () => {
const value = {
cmd: apiCmds.getCitrixMediaRedirectionStatus,
};
ipcMain.send(apiName.symphonyApi, value);
expect(getCitrixMediaRedirectionStatus).toBeCalled();
});
});
});

43
src/app/citrix-handler.ts Normal file
View File

@ -0,0 +1,43 @@
import { enumerateValues, HKEY, RegistryValueType } from 'registry-js';
import { isWindowsOS } from '../common/env';
export enum RedirectionStatus {
/**
* Citrix virtual environment is not active
*/
INACTIVE = 'inactive',
/**
* Citrix virtual environment is active and media redirection is supported
*/
SUPPORTED = 'supported',
/**
* Citrix virtual environment is active but media redirection is not supported
*/
UNSUPPORTED = 'unsupported',
}
export const getCitrixMediaRedirectionStatus = (): RedirectionStatus => {
if (!isWindowsOS) {
// Citrix virtual environments are not supported on non-Windows OSes
return RedirectionStatus.INACTIVE;
}
const values = enumerateValues(
HKEY.HKEY_CURRENT_USER,
'Software\\Citrix\\HDXMediaStream',
);
for (const value of values) {
if (value.name === 'MSTeamsRedirectionSupport') {
if (value.type === RegistryValueType.REG_DWORD && value.data === 1) {
return RedirectionStatus.SUPPORTED;
} else {
return RedirectionStatus.UNSUPPORTED;
}
}
}
return RedirectionStatus.INACTIVE;
};

View File

@ -18,6 +18,7 @@ import { activityDetection } from './activity-detection';
import { analytics } from './analytics-handler';
import appStateHandler from './app-state-handler';
import { autoUpdate } from './auto-update-handler';
import { getCitrixMediaRedirectionStatus } from './citrix-handler';
import { CloudConfigDataTypes, config, ICloudConfig } from './config-handler';
import { downloadHandler } from './download-handler';
import { mainEvents } from './main-event-handler';
@ -457,6 +458,8 @@ ipcMain.handle(
return browserWin.getNativeWindowHandle();
}
break;
case apiCmds.getCitrixMediaRedirectionStatus:
return getCitrixMediaRedirectionStatus();
}
return;
},

View File

@ -62,6 +62,7 @@ export enum apiCmds {
handleSwiftSearchMessageEvents = 'handle-shift-search-message-events',
onSwiftSearchMessage = 'on-shift-search-message',
getNativeWindowHandle = 'get-native-window-handle',
getCitrixMediaRedirectionStatus = 'get-citrix-media-redirection-status',
}
export enum apiName {

View File

@ -283,6 +283,13 @@
<input type="text" id="text-window-handle" />
<hr />
<br />
<hr />
<p>Citrix Media Redirection Status:</p>
<button id="get-citrix-media-redir">Get status</button>
<input type="text" id="text-citrix-media-redir" />
<hr />
<br />
</body>
<script>
window.name = 'main';
@ -329,6 +336,7 @@
restartApp: 'restart-app',
autoUpdate: 'auto-update',
getNativeWindowHandle: 'get-native-window-handle',
getCitrixMediaRedirectionStatus: 'get-citrix-media-redirection-status',
};
let requestId = 0;
@ -1202,5 +1210,22 @@
});
}
});
document
.getElementById('get-citrix-media-redir')
.addEventListener('click', () => {
const resultCallback = (status) => {
document.getElementById('text-citrix-media-redir').value = status;
};
if (window.ssf) {
window.ssf.getCitrixMediaRedirectionStatus().then(resultCallback);
} else if (window.manaSSF) {
window.manaSSF.getCitrixMediaRedirectionStatus().then(resultCallback);
} else {
postRequest(apiCmds.getCitrixMediaRedirectionStatus, null, {
successCallback: resultCallback,
});
}
});
</script>
</html>

View File

@ -89,6 +89,8 @@ if (ssfWindow.ssf) {
getZoomLevel: ssfWindow.ssf.getZoomLevel,
supportedSettings: ssfWindow.ssf.supportedSettings,
getNativeWindowHandle: ssfWindow.ssf.getNativeWindowHandle,
getCitrixMediaRedirectionStatus:
ssfWindow.ssf.getCitrixMediaRedirectionStatus,
});
}

View File

@ -5,6 +5,7 @@ import {
searchAPIVersion,
version,
} from '../../package.json';
import { RedirectionStatus } from '../app/citrix-handler';
import { IDownloadItem } from '../app/download-handler';
import {
apiCmds,
@ -740,6 +741,16 @@ export class SSFApi {
cmd: apiCmds.getNativeWindowHandle,
});
}
/**
* Retrieves the current status of Citrix' media redirection feature
* @returns status
*/
public getCitrixMediaRedirectionStatus(): Promise<RedirectionStatus> {
return ipcRenderer.invoke(apiName.symphonyApi, {
cmd: apiCmds.getCitrixMediaRedirectionStatus,
});
}
}
/**