new(core.gbapp): #387 adding /setupSecurity multiple tokens.
This commit is contained in:
parent
ff76a2f963
commit
db21ad6574
6 changed files with 121 additions and 42 deletions
|
@ -383,6 +383,12 @@ export class AdminDialog extends IGBDialog {
|
||||||
min.dialogs.add(
|
min.dialogs.add(
|
||||||
new WaterfallDialog('/setupSecurity', [
|
new WaterfallDialog('/setupSecurity', [
|
||||||
async step => {
|
async step => {
|
||||||
|
const tokenName = step.activeDialog.state.tokenName = step.options['tokenName'];
|
||||||
|
step.activeDialog.state.clientId = min.core.getParam<string>(min.instance, `${tokenName} Client ID`, null),
|
||||||
|
step.activeDialog.state.clientSecret = min.core.getParam<string>(min.instance, `${tokenName} Client Secret`, null),
|
||||||
|
step.activeDialog.state.host = min.core.getParam<string>(min.instance, `${tokenName} Host`, null),
|
||||||
|
step.activeDialog.state.tenant = min.core.getParam<string>(min.instance, `${tokenName} Tenant`, null)
|
||||||
|
|
||||||
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
|
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
|
||||||
return await step.beginDialog('/auth');
|
return await step.beginDialog('/auth');
|
||||||
} else {
|
} else {
|
||||||
|
@ -391,12 +397,19 @@ export class AdminDialog extends IGBDialog {
|
||||||
},
|
},
|
||||||
|
|
||||||
async step => {
|
async step => {
|
||||||
|
if (step.activeDialog.state.tokenName) {
|
||||||
|
return await step.next(step.options);
|
||||||
|
}
|
||||||
|
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
const prompt = Messages[locale].enter_authenticator_tenant;
|
const prompt = Messages[locale].enter_authenticator_tenant;
|
||||||
|
|
||||||
return await min.conversationalService.prompt(min, step, prompt);
|
return await min.conversationalService.prompt(min, step, prompt);
|
||||||
},
|
},
|
||||||
async step => {
|
async step => {
|
||||||
|
if (step.activeDialog.state.tokenName) {
|
||||||
|
return await step.next(step.options);
|
||||||
|
}
|
||||||
step.activeDialog.state.authenticatorTenant = step.result;
|
step.activeDialog.state.authenticatorTenant = step.result;
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
const prompt = Messages[locale].enter_authenticator_authority_host_url;
|
const prompt = Messages[locale].enter_authenticator_authority_host_url;
|
||||||
|
@ -404,25 +417,36 @@ export class AdminDialog extends IGBDialog {
|
||||||
return await min.conversationalService.prompt(min, step, prompt);
|
return await min.conversationalService.prompt(min, step, prompt);
|
||||||
},
|
},
|
||||||
async step => {
|
async step => {
|
||||||
|
|
||||||
step.activeDialog.state.authenticatorAuthorityHostUrl = step.result;
|
step.activeDialog.state.authenticatorAuthorityHostUrl = step.result;
|
||||||
|
|
||||||
|
const tokenName = step.activeDialog.state.tokenName;
|
||||||
|
|
||||||
min.instance.authenticatorTenant = step.activeDialog.state.authenticatorTenant;
|
if (tokenName){
|
||||||
min.instance.authenticatorAuthorityHostUrl = step.activeDialog.state.authenticatorAuthorityHostUrl;
|
step.activeDialog.state.clientId
|
||||||
|
step.activeDialog.state.clientSecret
|
||||||
|
step.activeDialog.state.tenant
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
min.instance.authenticatorAuthorityHostUrl = step.activeDialog.state.authenticatorAuthorityHostUrl;
|
||||||
|
min.instance.authenticatorTenant = step.activeDialog.state.authenticatorTenant;
|
||||||
|
}
|
||||||
|
|
||||||
await min.adminService.updateSecurityInfo(
|
await min.adminService.updateSecurityInfo(
|
||||||
min.instance.instanceId,
|
min.instance.instanceId,
|
||||||
step.activeDialog.state.authenticatorTenant,
|
tokenName? step.activeDialog.state.tenant:step.activeDialog.state.authenticatorTenant,
|
||||||
step.activeDialog.state.authenticatorAuthorityHostUrl
|
tokenName?step.activeDialog.state.host:step.activeDialog.state.authenticatorAuthorityHostUrl
|
||||||
);
|
);
|
||||||
|
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
const buf = Buffer.alloc(16);
|
const buf = Buffer.alloc(16);
|
||||||
const state = `${min.instance.instanceId}${crypto.randomFillSync(buf).toString('hex')}`;
|
const state = `${min.instance.instanceId}${crypto.randomFillSync(buf).toString('hex')}`;
|
||||||
|
|
||||||
min.adminService.setValue(min.instance.instanceId, 'AntiCSRFAttackState', state);
|
min.adminService.setValue(min.instance.instanceId, `${tokenName}AntiCSRFAttackState`, state);
|
||||||
|
|
||||||
const redirectUri = urlJoin(process.env.BOT_URL, min.instance.botId, '/token');
|
const redirectUri = urlJoin(process.env.BOT_URL, min.instance.botId, `/token?value=${tokenName}`);
|
||||||
const url = `https://login.microsoftonline.com/${step.activeDialog.state.authenticatorTenant}/oauth2/authorize?client_id=${min.instance.marketplaceId}&response_type=code&redirect_uri=${redirectUri}&scope=https://graph.microsoft.com/.default&state=${state}&response_mode=query`;
|
const scope = tokenName?'': 'https://graph.microsoft.com/.default';
|
||||||
|
const url = `https://login.microsoftonline.com/${step.activeDialog.state.authenticatorTenant}/oauth2/authorize?client_id=${min.instance.marketplaceId}&response_type=code&redirect_uri=${redirectUri}&scope=${scope}&state=${state}&response_mode=query`;
|
||||||
|
|
||||||
await min.conversationalService.sendText(min, step, Messages[locale].consent(url));
|
await min.conversationalService.sendText(min, step, Messages[locale].consent(url));
|
||||||
|
|
||||||
|
|
|
@ -230,17 +230,23 @@ export class GBAdminService implements IGBAdminService {
|
||||||
return obj.value;
|
return obj.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async acquireElevatedToken(instanceId: number, root: boolean = false): Promise<string> {
|
public async acquireElevatedToken(instanceId: number, root: boolean = false,
|
||||||
|
tokenName: string = null,
|
||||||
|
clientId: string = null,
|
||||||
|
clientSecret: string = null,
|
||||||
|
host: string = null,
|
||||||
|
tenant: string = null
|
||||||
|
): Promise<string> {
|
||||||
|
|
||||||
if (root) {
|
if (root) {
|
||||||
const minBoot = GBServer.globals.minBoot;
|
const minBoot = GBServer.globals.minBoot;
|
||||||
instanceId = minBoot.instance.instanceId;
|
instanceId = minBoot.instance.instanceId;
|
||||||
}
|
}
|
||||||
GBLog.info(`Acquiring token for instanceId: ${instanceId} (root: ${root}).`);
|
GBLog.info(`Acquiring token for instanceId: ${instanceId} ${tokenName} (root: ${root}).`);
|
||||||
|
|
||||||
let expiresOnV;
|
let expiresOnV;
|
||||||
try {
|
try {
|
||||||
expiresOnV = await this.getValue(instanceId, 'expiresOn');
|
expiresOnV = await this.getValue(instanceId, `${tokenName}expiresOn`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`/setupSecurity is required before running /publish.`);
|
throw new Error(`/setupSecurity is required before running /publish.`);
|
||||||
}
|
}
|
||||||
|
@ -250,23 +256,23 @@ export class GBAdminService implements IGBAdminService {
|
||||||
|
|
||||||
const expiresOn = new Date(expiresOnV);
|
const expiresOn = new Date(expiresOnV);
|
||||||
if (expiresOn.getTime() > new Date().getTime()) {
|
if (expiresOn.getTime() > new Date().getTime()) {
|
||||||
const accessToken = await this.getValue(instanceId, 'accessToken');
|
const accessToken = await this.getValue(instanceId, `${tokenName}accessToken`);
|
||||||
resolve(accessToken);
|
resolve(accessToken);
|
||||||
} else {
|
} else {
|
||||||
const authorizationUrl = urlJoin(
|
const authorizationUrl = urlJoin(
|
||||||
instance.authenticatorAuthorityHostUrl,
|
host ? host : instance.authenticatorAuthorityHostUrl,
|
||||||
instance.authenticatorTenant,
|
tenant ? tenant : instance.authenticatorTenant,
|
||||||
'/oauth2/authorize'
|
'/oauth2/authorize'
|
||||||
);
|
);
|
||||||
|
|
||||||
const refreshToken = await this.getValue(instanceId, 'refreshToken');
|
const refreshToken = await this.getValue(instanceId, `${tokenName}refreshToken`);
|
||||||
const resource = 'https://graph.microsoft.com';
|
const resource = clientId? '': 'https://graph.microsoft.com';
|
||||||
const authenticationContext = new AuthenticationContext(authorizationUrl);
|
const authenticationContext = new AuthenticationContext(authorizationUrl);
|
||||||
|
|
||||||
authenticationContext.acquireTokenWithRefreshToken(
|
authenticationContext.acquireTokenWithRefreshToken(
|
||||||
refreshToken,
|
refreshToken,
|
||||||
instance.marketplaceId,
|
clientId ? clientId : instance.marketplaceId,
|
||||||
instance.marketplacePassword,
|
clientId ? clientSecret : instance.marketplacePassword,
|
||||||
resource,
|
resource,
|
||||||
async (err, res) => {
|
async (err, res) => {
|
||||||
if (err !== null) {
|
if (err !== null) {
|
||||||
|
@ -274,9 +280,9 @@ export class GBAdminService implements IGBAdminService {
|
||||||
} else {
|
} else {
|
||||||
const token = res as TokenResponse;
|
const token = res as TokenResponse;
|
||||||
try {
|
try {
|
||||||
await this.setValue(instanceId, 'accessToken', token.accessToken);
|
await this.setValue(instanceId, `${tokenName}accessToken`, token.accessToken);
|
||||||
await this.setValue(instanceId, 'refreshToken', token.refreshToken);
|
await this.setValue(instanceId, `${tokenName}refreshToken`, token.refreshToken);
|
||||||
await this.setValue(instanceId, 'expiresOn', token.expiresOn.toString());
|
await this.setValue(instanceId, `${tokenName}expiresOn`, token.expiresOn.toString());
|
||||||
resolve(token.accessToken);
|
resolve(token.accessToken);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
reject(err);
|
reject(err);
|
||||||
|
|
|
@ -719,15 +719,36 @@ export class GBVMService extends GBService {
|
||||||
GBConfigService.get('DEFAULT_CONTENT_LANGUAGE')
|
GBConfigService.get('DEFAULT_CONTENT_LANGUAGE')
|
||||||
);
|
);
|
||||||
|
|
||||||
// These variables will be automatically be available as normal BASIC variables.
|
|
||||||
|
|
||||||
let variables = [];
|
let variables = [];
|
||||||
|
|
||||||
|
// Find all tokens in .gbot Config.
|
||||||
|
|
||||||
|
const strFind = ' Client ID';
|
||||||
|
const tokens = await min.core['findParam'](min.instance, strFind);
|
||||||
|
await CollectionUtil.asyncForEach(tokens,async t => {
|
||||||
|
const tokenName = t.replace(strFind, '');
|
||||||
|
try {
|
||||||
|
variables[t] = await (min.adminService as any)['acquireElevatedToken']
|
||||||
|
(min.instance.instanceId, false,
|
||||||
|
min.core.getParam<string>(min.instance, `${tokenName} Client ID`, null),
|
||||||
|
min.core.getParam<string>(min.instance, `${tokenName} Client Secret`, null),
|
||||||
|
min.core.getParam<string>(min.instance, `${tokenName} Host`, null),
|
||||||
|
min.core.getParam<string>(min.instance, `${tokenName} Tenant`, null)
|
||||||
|
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
variables[t] = 'ERROR: Configure /setupSecurity before using token variables.';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// These variables will be automatically be available as normal BASIC variables.
|
||||||
|
|
||||||
try {
|
try {
|
||||||
variables['aadToken'] = await (min.adminService as any)['acquireElevatedToken'](min.instance.instanceId, false);
|
variables['aadToken'] = await (min.adminService as any)['acquireElevatedToken']
|
||||||
|
(min.instance.instanceId, false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
variables['aadToken'] = 'ERROR: Configure /setupSecurity before using aadToken variable.';
|
variables['aadToken'] = 'ERROR: Configure /setupSecurity before using aadToken variable.';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds all .gbot params as variables.
|
// Adds all .gbot params as variables.
|
||||||
|
|
||||||
|
|
|
@ -400,7 +400,7 @@ ENDPOINT_UPDATE=true
|
||||||
public async getApplicationsByInstanceId(appPackages, instanceId: number) {
|
public async getApplicationsByInstanceId(appPackages, instanceId: number) {
|
||||||
const options = { where: { instanceId: instanceId } };
|
const options = { where: { instanceId: instanceId } };
|
||||||
const apps = await GuaribasApplications.findAll(options);
|
const apps = await GuaribasApplications.findAll(options);
|
||||||
|
|
||||||
let matchingAppPackages = [];
|
let matchingAppPackages = [];
|
||||||
await CollectionUtil.asyncForEach(appPackages, async appPackage => {
|
await CollectionUtil.asyncForEach(appPackages, async appPackage => {
|
||||||
const filenameOnly = Path.basename(appPackage.name);
|
const filenameOnly = Path.basename(appPackage.name);
|
||||||
|
@ -409,10 +409,10 @@ ENDPOINT_UPDATE=true
|
||||||
matchingAppPackages.push(appPackage);
|
matchingAppPackages.push(appPackage);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return matchingAppPackages;
|
return matchingAppPackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all bot instances from object storage, if it's formatted.
|
* Loads all bot instances from object storage, if it's formatted.
|
||||||
*/
|
*/
|
||||||
|
@ -429,7 +429,7 @@ ENDPOINT_UPDATE=true
|
||||||
await CollectionUtil.asyncForEach(instances, async instance => {
|
await CollectionUtil.asyncForEach(instances, async instance => {
|
||||||
GBLog.info(`Updating bot endpoint for ${instance.botId}...`);
|
GBLog.info(`Updating bot endpoint for ${instance.botId}...`);
|
||||||
try {
|
try {
|
||||||
|
|
||||||
await installationDeployer.updateBotProxy(
|
await installationDeployer.updateBotProxy(
|
||||||
instance.botId,
|
instance.botId,
|
||||||
GBConfigService.get('CLOUD_GROUP'),
|
GBConfigService.get('CLOUD_GROUP'),
|
||||||
|
@ -698,4 +698,25 @@ ENDPOINT_UPDATE=true
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a dynamic param from instance. *
|
||||||
|
*/
|
||||||
|
public async findParam<T>(instance: IGBInstance, criteria: string) {
|
||||||
|
let params = null;
|
||||||
|
const list = [];
|
||||||
|
if (instance.params) {
|
||||||
|
params = JSON.parse(instance.params);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(params).forEach(e => {
|
||||||
|
if (criteria.toLowerCase().indexOf(e.toLowerCase())) {
|
||||||
|
list.push(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -491,16 +491,27 @@ export class GBMinService {
|
||||||
*/
|
*/
|
||||||
private handleOAuthTokenRequests(server: any, min: GBMinInstance, instance: IGBInstance) {
|
private handleOAuthTokenRequests(server: any, min: GBMinInstance, instance: IGBInstance) {
|
||||||
server.get(`/${min.instance.botId}/token`, async (req, res) => {
|
server.get(`/${min.instance.botId}/token`, async (req, res) => {
|
||||||
|
|
||||||
|
const tokenName = req.params['value'];
|
||||||
|
|
||||||
// Checks request state by reading AntiCSRFAttackState from GB Admin infrastructure.
|
// Checks request state by reading AntiCSRFAttackState from GB Admin infrastructure.
|
||||||
|
|
||||||
const state = await min.adminService.getValue(instance.instanceId, 'AntiCSRFAttackState');
|
const state = await min.adminService.getValue(instance.instanceId, `${tokenName}AntiCSRFAttackState`);
|
||||||
if (req.query.state !== state) {
|
if (req.query.state !== state) {
|
||||||
const msg = 'WARNING: state field was not provided as anti-CSRF token';
|
const msg = 'WARNING: state field was not provided as anti-CSRF token';
|
||||||
GBLog.error(msg);
|
GBLog.error(msg);
|
||||||
throw new Error(msg);
|
throw new Error(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clientId = min.core.getParam<string>(min.instance, `${tokenName} Client ID`, null),
|
||||||
|
const clientSecret = min.core.getParam<string>(min.instance, `${tokenName} Client Secret`, null),
|
||||||
|
const host = min.core.getParam<string>(min.instance, `${tokenName} Host`, null),
|
||||||
|
const tenant = min.core.getParam<string>(min.instance, `${tokenName} Tenant`, null)
|
||||||
|
|
||||||
const authenticationContext = new AuthenticationContext.AuthenticationContext(
|
const authenticationContext = new AuthenticationContext.AuthenticationContext(
|
||||||
urlJoin(min.instance.authenticatorAuthorityHostUrl, min.instance.authenticatorTenant)
|
urlJoin(
|
||||||
|
tokenName? host: min.instance.authenticatorAuthorityHostUrl,
|
||||||
|
tokenName? tenant: min.instance.authenticatorTenant)
|
||||||
);
|
);
|
||||||
const resource = 'https://graph.microsoft.com';
|
const resource = 'https://graph.microsoft.com';
|
||||||
|
|
||||||
|
@ -510,20 +521,21 @@ export class GBMinService {
|
||||||
req.query.code,
|
req.query.code,
|
||||||
urlJoin(process.env.BOT_URL, min.instance.botId, '/token'),
|
urlJoin(process.env.BOT_URL, min.instance.botId, '/token'),
|
||||||
resource,
|
resource,
|
||||||
instance.marketplaceId,
|
tokenName? clientId: instance.marketplaceId,
|
||||||
instance.marketplacePassword,
|
tokenName? clientSecret: instance.marketplacePassword,
|
||||||
async (err, token) => {
|
async (err, token) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
const msg = `handleOAuthTokenRequests: Error acquiring token: ${err}`;
|
const msg = `handleOAuthTokenRequests: Error acquiring token: ${err}`;
|
||||||
GBLog.error(msg);
|
GBLog.error(msg);
|
||||||
res.send(msg);
|
res.send(msg);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Saves token to the database.
|
// Saves token to the database.
|
||||||
|
|
||||||
await this.adminService.setValue(instance.instanceId, 'accessToken', token['accessToken']);
|
await this.adminService.setValue(instance.instanceId, `${tokenName}accessToken`, token['accessToken']);
|
||||||
await this.adminService.setValue(instance.instanceId, 'refreshToken', token['refreshToken']);
|
await this.adminService.setValue(instance.instanceId, `${tokenName}refreshToken`, token['refreshToken']);
|
||||||
await this.adminService.setValue(instance.instanceId, 'expiresOn', token['expiresOn'].toString());
|
await this.adminService.setValue(instance.instanceId, `${tokenName}expiresOn`, token['expiresOn'].toString());
|
||||||
await this.adminService.setValue(instance.instanceId, 'AntiCSRFAttackState', null);
|
await this.adminService.setValue(instance.instanceId, `${tokenName}AntiCSRFAttackState`, null);
|
||||||
|
|
||||||
// Inform the home for default .gbui after finishing token retrival.
|
// Inform the home for default .gbui after finishing token retrival.
|
||||||
|
|
||||||
|
|
|
@ -1149,11 +1149,6 @@ export class WhatsappDirectLine extends GBService {
|
||||||
await sec.updateUserHearOnDialog(user.userId, null);
|
await sec.updateUserHearOnDialog(user.userId, null);
|
||||||
await (activeMin as any).whatsAppDirectLine.received(req, res);
|
await (activeMin as any).whatsAppDirectLine.received(req, res);
|
||||||
} else {
|
} else {
|
||||||
await (activeMin as any).whatsAppDirectLine.sendToDevice(
|
|
||||||
id,
|
|
||||||
`Olá! Seja bem-vinda(o)!\nMe chamo ${activeMin.instance.title}. Como posso ajudar? Pode me falar que eu te ouço, me manda um aúdio.`,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
if (res) {
|
if (res) {
|
||||||
res.end();
|
res.end();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue