Skip to content
This repository has been archived by the owner on May 22, 2020. It is now read-only.

Commit

Permalink
RUN-2959: query desktop owner settings from RVM (#122)
Browse files Browse the repository at this point in the history
* RUN-2959: Populate API policy from RVM in Message PreProcessor

* RUN-2959: Populate API policy from RVM in Message PreProcessor

* RUN-2959: Populate API policy from RVM in Message PreProcessor

* RUN-2959: Populate API policy from RVM in Message PreProcessor
  • Loading branch information
wenjunche authored and StevenEBarbaro committed May 2, 2017
1 parent 3d85eda commit 43882fa
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 13 deletions.
160 changes: 147 additions & 13 deletions src/browser/api_protocol/api_handlers/api_policy_processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,25 @@ Please contact OpenFin Inc. at [email protected] to obtain a Commercial License.
import {MessagePackage} from '../transport_strategy/api_transport_base';
const coreState = require('../../core_state');
const electronApp = require('electron').app;
const system = require('../../api/system').System;
const apiProtocolBase = require('./api_protocol_base');
const rvmBus = require('../../rvm/rvm_message_bus'); // retrieve permission setting from registry
const configUrlPermissionsMap : { [url: string]: any } = {}; // cached configUrl => permission object, retrieved from RVM
// if a configUrl is mapped to a boolean true, request to RVM is successful
// did not return permissions
const CONFIG_URL_WILDCARD = 'default'; // can set as default for all applications. Checked ONLY IF permissions
// for a particular URL is not defined
const ENABLE_DESKTOP_OWNER_SETTINGS: string = 'enable-desktop-owner-settings'; // RVM adds this to runtime->arguments
// if settings are detected in Registry
const DESKTOP_OWNER_SETTINGS_TIMEOUT: string = 'desktop-owner-settings-timeout'; // timeout for requesting from RVM in ms
let desktopOwnerSettingsTimeout: number = 2000; // in ms
let desktopOwnerSettingEnabled: boolean = false;

enum POLICY_AUTH_RESULT {
Allowed = 1,
Denied,
NotDefined // config URL not found in policy. Check window options instead
}

// actionAPINameMap has all APIs that must be authorized
// @todo this map should be set in group policy or some other way
Expand Down Expand Up @@ -84,7 +102,7 @@ function checkWindowPermissions(apiPath: [string], windowPermissions: any, isChi
}
level += 1;
}
electronApp.vlog(1, `checkWindowPermissions level ${level} value ${lastValue}`);
electronApp.vlog(1, `checkWindowPermissions level ${level}`);
if (level === apiPath.length && typeof lastValue === 'boolean') {
permitted = !!lastValue;
}
Expand All @@ -93,19 +111,20 @@ function checkWindowPermissions(apiPath: [string], windowPermissions: any, isChi
}

/**
* Authorize the action for a window sending the action
* Authorize the action for a window sending the action based on window options
*
* @param windowOpts window options
* @param parentUuid uuid of parent app
* @param action in message
* @returns {boolean} true if authorized
* @returns {Promise<boolean>} resolves true if authorized
*/
function authorizeAction(windowOpts: any, parentUuid: string, action: string): boolean {
function authorizeActionFromWindowOptions(windowOpts: any, parentUuid: string, action: string): boolean {
electronApp.vlog(1, `authorizeAction ${action} for ${windowOpts.uuid} ${windowOpts.name}`);
const apiPath: [string] = actionAPINameMap[action];
let allowed: boolean = true;
if (actionAPINameMap.hasOwnProperty(action)) { // if listed in the map, has to be checked
const isChildWindow = windowOpts.uuid !== windowOpts.name;
allowed = isChildWindow;
const apiPath: [string] = actionAPINameMap[action];
if (windowOpts && windowOpts.permissions) {
allowed = checkWindowPermissions(apiPath, windowOpts.permissions, isChildWindow);
}
Expand All @@ -116,7 +135,8 @@ function authorizeAction(windowOpts: any, parentUuid: string, action: string): b
const parentOpts = parentObject._options;
if (parentOpts) {
electronApp.vlog(1, `authorizeAction checks parent ${parentUuid}`);
allowed = authorizeAction(parentOpts, parentObject.parentUuid, action);
allowed = authorizeActionFromWindowOptions(parentOpts, parentObject.parentUuid, action);
return;
} else {
electronApp.vlog(1, `authorizeAction missing parent options ${parentUuid}`);
}
Expand All @@ -127,8 +147,58 @@ function authorizeAction(windowOpts: any, parentUuid: string, action: string): b
return allowed;
}

/**
* Authorize the action for a window sending the action based on policies supplied by RVM
*
* @param windowOpts
* @param action
* @returns {Promise<boolean>}
* @returns {Promise<string>} resolves with POLICY_AUTH_RESULT
*/
function authorizeActionFromPolicy(windowOpts: any, action: string): Promise<POLICY_AUTH_RESULT> {
electronApp.vlog(1, `authorizeActionFromPolicy ${action} for ${windowOpts.uuid} ${windowOpts.name}`);
const apiPath: [string] = actionAPINameMap[action];
return new Promise((resolve, reject) => {
if (desktopOwnerSettingEnabled === true) {
const configUrl = coreState.getConfigUrlByUuid(windowOpts.uuid);
if (configUrl) {
electronApp.vlog(1, `authorizeActionFromPolicy checking with config url ${configUrl}`);
requestAppPermissions(configUrl).then((resultByUrl: any) => {
if (resultByUrl.permissions) {
resolve(checkWindowPermissions(apiPath, resultByUrl.permissions, false) ?
POLICY_AUTH_RESULT.Allowed : POLICY_AUTH_RESULT.Denied);
} else { // check default permissions defined with CONFIG_URL_WILDCARD
electronApp.vlog(1, `authorizeActionFromPolicy checking with RVM ${CONFIG_URL_WILDCARD}`);
requestAppPermissions(CONFIG_URL_WILDCARD).then((resultByDefault: any) => {
if (resultByDefault.permissions) {
resolve(checkWindowPermissions(apiPath, resultByDefault.permissions, false) ? POLICY_AUTH_RESULT.Allowed :
POLICY_AUTH_RESULT.Denied);
} else {
resolve(POLICY_AUTH_RESULT.NotDefined); // config URL not defined in policy
}
}).catch((error: any) => {
electronApp.vlog(1, `authorizeActionFromPolicy query for permissions failed ${CONFIG_URL_WILDCARD}`);
reject(false);
});
}
}).catch((error: any) => {
electronApp.vlog(1, `authorizeActionFromPolicy query for permissions failed ${configUrl}`);
reject(false);
});
} else {
electronApp.vlog(1, 'authorizeActionFromPolicy configUrl not defined');
resolve(POLICY_AUTH_RESULT.NotDefined); // config URL not defined in policy
}
} else {
electronApp.vlog(1, `authorizeActionFromPolicy desktopOwnerSettingEnabled ${desktopOwnerSettingEnabled}`);
resolve(POLICY_AUTH_RESULT.NotDefined); // config URL not defined in policy
}
});
}

/**
* Message pre-processor
*
* @param msg message package to check
* @param next function to call if ok to proceed
*/
Expand All @@ -144,20 +214,84 @@ function apiPolicyPreProcessor(msg: MessagePackage, next: () => void): void {
const appObject = coreState.getAppObjByUuid(identity.uuid);
// parentUuid for child windows is uuid of the app
const parentUuid = identity.uuid === identity.name ? appObject.parentUuid : identity.uuid;
if (!authorizeAction(coreState.getWindowOptionsById(originWindow.id), parentUuid, action)) {
electronApp.vlog(1, `apiPolicyPreProcessor rejecting ${action} from ${identity.uuid} ${identity.name}`);
authorizeActionFromPolicy(coreState.getWindowOptionsById(originWindow.id), action).then((result: POLICY_AUTH_RESULT) => {
if (result === POLICY_AUTH_RESULT.Allowed) {
next();
} else if (result === POLICY_AUTH_RESULT.Denied) {
electronApp.vlog(1, `apiPolicyPreProcessor rejecting from policy ${action} from ${identity.uuid} ${identity.name}`);
nack('Rejected, action is not authorized');
} else {
if (authorizeActionFromWindowOptions(coreState.getWindowOptionsById(originWindow.id), parentUuid, action)) {
next();
} else {
electronApp.vlog(1, `apiPolicyPreProcessor rejecting from win opts ${action} from ${identity.uuid} ` +
`${identity.name}`);
nack('Rejected, action is not authorized');
}
}
}).catch(() => {
electronApp.vlog(1, `apiPolicyPreProcessor rejecting from error ${action} from ${identity.uuid} ${identity.name}`);
nack('Rejected, action is not authorized');
return;
}
});
} else {
electronApp.vlog(1, `apiPolicyPreProcessor missing origin window ${action} from ${identity.uuid} ${identity.name}`);
next();
}
} else {
next();
}
} else {
next();
}
next();
}

if (electronApp.getCommandLineArguments().includes('--enable-strict-api-permissions')) {
electronApp.log('info', 'Installing API policy PreProcessor');
/**
* Get application permissions from cache or RVM
*
* @param configUrl url of startup manifest
* @returns {Promise<any>} resolves with permissions defined in application assets;
* reject if request to RVM failed
*/
function requestAppPermissions(configUrl: string): Promise<any> {
electronApp.vlog(1, `requestAppPermissions ${configUrl} `);
return new Promise((resolve, reject) => {
if (configUrlPermissionsMap[configUrl]) {
electronApp.vlog(1, `requestAppPermissions cached ${configUrl} `);
resolve(configUrlPermissionsMap[configUrl]);
} else {
rvmBus.send('application', {
action: 'get-desktop-owner-settings',
sourceUrl: configUrl
}, (rvmResponse: any) => {
electronApp.vlog(1, `requestAppPermissions from RVM ${JSON.stringify(rvmResponse)} `);
if (rvmResponse.payload && rvmResponse.payload.success === true &&
rvmResponse.payload.payload) {
if (rvmResponse.payload.payload.permissions) {
configUrlPermissionsMap[configUrl] = {permissions: rvmResponse.payload.payload.permissions};
// cache it
} else {
// if permissions is missing, startup URL is not defined in desktop-owner-settings. Not an error
configUrlPermissionsMap[configUrl] = {};
}
resolve(configUrlPermissionsMap[configUrl]);
} else {
system.log('error', `requestAppPermissions from RVM failed ${JSON.stringify(rvmResponse)}`);
reject(rvmResponse); // false indicates request to RVM failed
}
}, desktopOwnerSettingsTimeout / 1000);
}
});
}

if (coreState.argo['enable-strict-api-permissions']) {
electronApp.log('info', `Installing API policy PreProcessor ${JSON.stringify(coreState.getStartManifest())}`);
apiProtocolBase.getDefaultRequestHandler().addPreProcessor(apiPolicyPreProcessor);
desktopOwnerSettingEnabled = !!coreState.argo[ENABLE_DESKTOP_OWNER_SETTINGS];
electronApp.vlog(1, `desktopOwnerSettingEnabled ${desktopOwnerSettingEnabled}`);
if (desktopOwnerSettingEnabled === true && coreState.argo[DESKTOP_OWNER_SETTINGS_TIMEOUT]) {
desktopOwnerSettingsTimeout = Number(coreState.argo[DESKTOP_OWNER_SETTINGS_TIMEOUT]);
electronApp.vlog(1, `desktopOwnerSettingsTimeout ${desktopOwnerSettingsTimeout}`);
}
}

export {apiPolicyPreProcessor};
9 changes: 9 additions & 0 deletions src/browser/core_state.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,14 @@ function getUuidBySourceUrl(sourceUrl) {
return app && app.appObj && app.appObj.uuid;
}

function getConfigUrlByUuid(uuid) {
let app = appByUuid(uuid);
while (app && app.appObj && app.appObj.parentUuid) {
app = appByUuid(app.appObj.parentUuid);
}
return app && app._configUrl;
}

function setAppObj(appId, appObj) {
const app = getAppById(appId);

Expand Down Expand Up @@ -506,6 +514,7 @@ module.exports = {
getAppObj,
getAppObjByUuid,
getUuidBySourceUrl,
getConfigUrlByUuid,
getAppRunningState,
getAppRestartingState,
getChildrenByApp,
Expand Down

0 comments on commit 43882fa

Please sign in to comment.