-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
enable execution in attended mode (#173)
* enable execution in attended mode * 2.6.60
- Loading branch information
1 parent
a2fe6ea
commit 33cb963
Showing
9 changed files
with
241 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { DataContext } from "./types"; | ||
import {ConfigurationBase} from '@themost/common'; | ||
|
||
export declare interface ConfigurableApplication { | ||
getConfiguration(): ConfigurationBase | ||
} | ||
|
||
export declare function executeInUnattendedMode(context: DataContext, func: (callback: (err?: Error) => void) => void, callback: (err?: Error) => void): void; | ||
export declare function executeInUnattendedModeAsync(context: DataContext, func: () => Promise<void>): Promise<void>; | ||
export declare function enableUnattendedExecution(app: ConfigurableApplication, account?: string): void; | ||
export declare function disableUnattendedExecution(app: ConfigurableApplication): void; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
var unattendedMode = Symbol('unattendedMode'); | ||
var {RandomUtils} = require('@themost/common'); | ||
|
||
/** | ||
* Execute a callable function with elevated privileges in unattended mode. | ||
* @param {import('./types').DataContext} context | ||
* @param {function(function(Error?): void)} callable | ||
* @param {function(Error?): void} callback | ||
* @returns {void} | ||
*/ | ||
function executeInUnattendedMode(context, callable, callback) { | ||
if (typeof callable !== 'function') { | ||
return callback(new Error('Unattended mode requires a callable function')); | ||
} | ||
// if the context is already in unattended mode | ||
if (context[unattendedMode]) { | ||
try { | ||
// execute callable function | ||
void callable(function (err) { | ||
return callback(err); | ||
}); | ||
} catch (err) { | ||
return callback(err); | ||
} | ||
} else { | ||
var interactiveUser; | ||
try { | ||
const account = context.getConfiguration().getSourceAt('settings/auth/unattendedExecutionAccount'); | ||
if (account == null) { | ||
return callback(new Error('The unattended execution account is not defined. The operation cannot be completed.')); | ||
} | ||
// enter unattended mode | ||
context[unattendedMode] = true; | ||
// get interactive user | ||
if (context.user) { | ||
interactiveUser = Object.assign({}, context.user); | ||
// set interactive user | ||
context.interactiveUser = interactiveUser; | ||
} | ||
if (account) { | ||
context.user = {name: account, authenticationType: 'Basic'}; | ||
} | ||
void callable(function (err) { | ||
// restore user | ||
if (interactiveUser) { | ||
context.user = Object.assign({}, interactiveUser); | ||
} | ||
delete context.interactiveUser; | ||
// exit unattended mode | ||
delete context[unattendedMode]; | ||
return callback(err); | ||
}); | ||
} catch (err) { | ||
// restore user | ||
if (interactiveUser) { | ||
context.user = Object.assign({}, interactiveUser); | ||
} | ||
delete context.interactiveUser; | ||
// exit unattended mode | ||
delete context[unattendedMode]; | ||
return callback(err); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Execute a callable function with elevated privileges in unattended mode. | ||
* @param {import('./types').DataContext} context | ||
* @param {function(): Promise<void>} callable | ||
* @returns {Promise<void>} | ||
*/ | ||
function executeInUnattendedModeAsync(context, callable) { | ||
return new Promise((resolve, reject) => { | ||
void executeInUnattendedMode(context, function (cb) { | ||
return callable().then(function () { | ||
return cb(); | ||
}).catch(function (err) { | ||
return cb(err); | ||
}); | ||
}, function (err) { | ||
if (err) { | ||
return reject(err); | ||
} | ||
return resolve(); | ||
}); | ||
}); | ||
} | ||
|
||
/** | ||
* Enables unattended mode | ||
* @param {{getConfiguration(): import('@themost/common').ConfigurationBase}} app | ||
* @param {string=} executionAccount | ||
*/ | ||
function enableUnattendedExecution(app, executionAccount) { | ||
app.getConfiguration().setSourceAt('settings/auth/unattendedExecutionAccount', executionAccount || RandomUtils.randomChars(14)); | ||
} | ||
/** | ||
* Disables unattended mode | ||
* @param {{getConfiguration(): import('@themost/common').ConfigurationBase}} app | ||
*/ | ||
function disableUnattendedExecution(app) { | ||
app.getConfiguration().setSourceAt('settings/auth/unattendedExecutionAccount', null); | ||
} | ||
|
||
module.exports = { | ||
executeInUnattendedMode, | ||
executeInUnattendedModeAsync, | ||
enableUnattendedExecution, | ||
disableUnattendedExecution | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import {TestApplication} from './TestApplication'; | ||
import { | ||
DataContext, | ||
disableUnattendedExecution, | ||
enableUnattendedExecution, | ||
executeInUnattendedModeAsync | ||
} from '@themost/data'; | ||
import {resolve} from 'path'; | ||
|
||
describe('UnattendedMode', () => { | ||
let app: TestApplication; | ||
let context: DataContext; | ||
beforeAll((done) => { | ||
app = new TestApplication(resolve(__dirname, 'test2')); | ||
context = app.createContext(); | ||
return done(); | ||
}); | ||
afterAll(async () => { | ||
await context.finalizeAsync(); | ||
await app.finalize(); | ||
}); | ||
it('should execute in unattended mode', async () => { | ||
// set context user | ||
context.user = { | ||
name: '[email protected]' | ||
}; | ||
const items = await context.model('Order').where('orderedItem/name').equal( | ||
'Razer Blade (2013)' | ||
).getItems(); | ||
expect(items.length).toBeFalsy(); | ||
await executeInUnattendedModeAsync(context, async () => { | ||
expect(context.interactiveUser).toBeTruthy(); | ||
expect(context.interactiveUser.name).toEqual('[email protected]'); | ||
const items = await context.model('Order').where('orderedItem/name').equal( | ||
'Razer Blade (2013)' | ||
).getItems(); | ||
expect(items.length).toBeTruthy(); | ||
}); | ||
expect(context.interactiveUser).toBeFalsy(); | ||
}); | ||
|
||
it('should execute in unattended mode and get error', async () => { | ||
// set context user | ||
context.user = { | ||
name: '[email protected]' | ||
}; | ||
await expect(executeInUnattendedModeAsync(context, async () => { | ||
throw new Error('Custom error'); | ||
})).rejects.toThrow(); | ||
expect(context.interactiveUser).toBeFalsy(); | ||
expect(context.user.name).toEqual('[email protected]'); | ||
}); | ||
|
||
it('should execute in unattended mode in series', async () => { | ||
// set context user | ||
context.user = { | ||
name: '[email protected]' | ||
}; | ||
await executeInUnattendedModeAsync(context, async () => { | ||
expect(context.interactiveUser).toBeTruthy(); | ||
const items = await context.model('Order').where('orderedItem/name').equal( | ||
'Razer Blade (2013)' | ||
).getItems(); | ||
expect(items.length).toBeTruthy(); | ||
await executeInUnattendedModeAsync(context, async () => { | ||
expect(context.interactiveUser).toBeTruthy(); | ||
expect(context.interactiveUser.name).toEqual('[email protected]'); | ||
const items = await context.model('Order').where('orderedItem/name').equal( | ||
'Sony VAIO Flip 15' | ||
).getItems(); | ||
expect(items.length).toBeTruthy(); | ||
}); | ||
}); | ||
expect(context.interactiveUser).toBeFalsy(); | ||
expect(context.user.name).toEqual('[email protected]'); | ||
}); | ||
|
||
it('should disable unattended execution', async () => { | ||
// set context user | ||
context.user = { | ||
name: '[email protected]' | ||
}; | ||
disableUnattendedExecution(app); | ||
expect(app.getConfiguration().getSourceAt('settings/auth/unattendedExecutionAccount')).toBeFalsy(); | ||
await expect(executeInUnattendedModeAsync(context, async () => { | ||
await context.model('Order').where('orderedItem/name').equal( | ||
'Sony VAIO Flip 15' | ||
).getItems(); | ||
})).rejects.toThrow('The unattended execution account is not defined. The operation cannot be completed.'); | ||
expect(context.interactiveUser).toBeFalsy(); | ||
enableUnattendedExecution(app); | ||
expect(app.getConfiguration().getSourceAt('settings/auth/unattendedExecutionAccount')).toBeTruthy(); | ||
await executeInUnattendedModeAsync(context, async () => { | ||
const items = await context.model('Order').where('orderedItem/name').equal( | ||
'Razer Blade (2013)' | ||
).getItems(); | ||
expect(items.length).toBeTruthy(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,9 @@ | |
"dom" | ||
], | ||
"paths": { | ||
"@themost/data": [ | ||
"./index" | ||
] | ||
} | ||
}, | ||
"exclude": [ | ||
|