diff --git a/packages/admin.gbapp/services/GBAdminService.ts b/packages/admin.gbapp/services/GBAdminService.ts index b7df4b44..fb32e091 100644 --- a/packages/admin.gbapp/services/GBAdminService.ts +++ b/packages/admin.gbapp/services/GBAdminService.ts @@ -79,37 +79,6 @@ export class GBAdminService { return Promise.resolve(obj.value); } - public static async acquireMsGraphTokenFromUsername(username, password) { - return new Promise(async (resolve, reject) => { - let resource = "https://graph.microsoft.com"; - let authenticatorAuthorityHostUrl = "https://login.microsoftonline.com"; - let authenticatorTenant = "pragmatismo.onmicrosoft.com"; - let authenticatorClientId = "0ffice19-91d5-41ea-98a4-ceea1655cc0b"; - - let authorizationUrl = UrlJoin( - authenticatorAuthorityHostUrl, - authenticatorTenant, - "/oauth2/authorize" - ); - var authenticationContext = new AuthenticationContext(authorizationUrl); - - authenticationContext.acquireTokenWithUsernamePassword( - resource, - username, - password, - authenticatorClientId, - async (err, res) => { - if (err) { - reject(err); - } else { - let token = res as TokenResponse; - resolve(token.accessToken); - } - } - ); - }); - } - public async acquireElevatedToken(instanceId): Promise { return new Promise(async (resolve, reject) => { let instance = await this.core.loadInstanceById(instanceId); @@ -195,7 +164,22 @@ export class GBAdminService { maximumLength: 14 }; let password = passwordGenerator.generatePassword(options); - password = password.replace(/[=:;\?]/g, ""); + password = password.replace(/@[=:;\?]/g, "#"); return password; } + + public static getRndReadableIdentifier() { + const passwordGenerator = new PasswordGenerator(); + const options = { + upperCaseAlpha: false, + lowerCaseAlpha: true, + number: false, + specialCharacter: false, + minimumLength: 12, + maximumLength: 14 + }; + let name = passwordGenerator.generatePassword(options); + return name; + } + } diff --git a/packages/azuredeployer.gbapp/services/AzureDeployerService.ts b/packages/azuredeployer.gbapp/services/AzureDeployerService.ts index f31aff1e..07bb27a8 100644 --- a/packages/azuredeployer.gbapp/services/AzureDeployerService.ts +++ b/packages/azuredeployer.gbapp/services/AzureDeployerService.ts @@ -81,8 +81,10 @@ export class AzureDeployerService extends GBService { let culture = "en-us"; // Tries do get information from .env file otherwise asks in command-line. + let instance: IGBInstance = {}; instance = await this.ensureConfiguration(instance); + instance.marketplacePassword = GBAdminService.getRndPassword(); let spinner = new Spinner("%s"); spinner.start(); @@ -91,12 +93,19 @@ export class AzureDeployerService extends GBService { let keys: any; let name = instance.botId; - instance.marketplacePassword = GBAdminService.getRndPassword(); - await this.internalDeployApp(name, instance.marketplacePassword) - logger.info(`Deploying Deploy Group (It may take a few minutes)...`); await this.createDeployGroup(name, instance.cloudLocation); + instance = await this.deployBootBot( + instance, + name, + `${proxyAddress}/api/messages/${name}`, + instance.nlpAppId, + instance.nlpKey, + instance.cloudSubscriptionId + ); + + logger.info(`Deploying Bot Server...`); let serverFarm = await this.createHostingPlan( name, @@ -110,10 +119,9 @@ export class AzureDeployerService extends GBService { instance.cloudLocation ); - let administratorLogin = `sa${GBAdminService.getRndPassword()}`; - let administratorPassword = GBAdminService.getRndPassword(); - logger.info(`Deploying Bot Storage...`); + let administratorLogin = `sa${GBAdminService.getRndReadableIdentifier()}`; + let administratorPassword = GBAdminService.getRndPassword(); let storageServer = `${name}-storage-server`; let storageName = `${name}-storage`; await this.createStorageServer( @@ -124,7 +132,6 @@ export class AzureDeployerService extends GBService { storageServer, instance.cloudLocation ); - await this.createStorage( name, storageServer, @@ -195,7 +202,7 @@ export class AzureDeployerService extends GBService { culture, instance.nlpAuthoringKey ); - let appId = GBAdminService.generateUuid(); + instance.nlpEndpoint = nlp.endpoint; instance.nlpKey = keys.key1; instance.nlpAppId = nlpAppId; @@ -205,10 +212,9 @@ export class AzureDeployerService extends GBService { instance, name, `${proxyAddress}/api/messages/${name}`, - null, - null, - instance.cloudSubscriptionId, - appId + instance.nlpAppId, + instance.nlpKey, + instance.cloudSubscriptionId ); spinner.stop(); @@ -371,9 +377,34 @@ export class AzureDeployerService extends GBService { endpoint, nlpAppId, nlpKey, - subscriptionId, - appId + subscriptionId ) { + let appId = GBConfigService.get("MSAPP_ID"); + let appPassword = GBConfigService.get("MSAPP_PASSWORD"); + + if (!appId || !appPassword) { + process.stdout.write( + "Sorry, this part cannot be automated yet due to Microsoft schedule, please go to https://apps.dev.microsoft.com/portal/register-app to generate manually an App ID and App Secret.\n" + ); + } + + let retriveAppId = () => { + if (!appId) { + process.stdout.write("Generated Application Id (MSAPP_ID):"); + appId = scanf("%s").replace(/(\n|\r)+$/, ""); + } + }; + + let retriveAppPassword = () => { + if (!appPassword) { + process.stdout.write("Generated Password (MSAPP_PASSWORD):"); + appPassword = scanf("%s").replace(/(\n|\r)+$/, ""); + } + }; + + retriveAppId(); + retriveAppPassword(); + await this.internalDeployBot( instance, this.accessToken, @@ -385,10 +416,12 @@ export class AzureDeployerService extends GBService { "global", nlpAppId, nlpKey, - subscriptionId, - appId + appId, + appPassword, + subscriptionId ); instance.marketplaceId = appId; + instance.marketplacePassword = appPassword; instance.botId = botId; return instance; @@ -424,46 +457,12 @@ export class AzureDeployerService extends GBService { req.headers = {}; req.headers["Content-Type"] = "application/json; charset=utf-8"; req.headers["accept-language"] = "*"; - req.headers["x-ms-client-request-id"] = GBAdminService.generateUuid(); req.headers["Authorization"] = "Bearer " + accessToken; let httpClient = new ServiceClient(); let res = await httpClient.sendRequest(req); + // TODO: Check res for error. } - - private async internalDeployApp(name, pass) { - - let username = GBConfigService.get("CLOUD_USERNAME"); - let password = GBConfigService.get("CLOUD_PASSWORD"); - let accessToken = await GBAdminService.acquireMsGraphTokenFromUsername(username, password); - - let graphUrl = "https://graph.microsoft.com"; - let expiresOn = new Date().toISOString(); - - let parameters = { - displayName: name, - passwordCredentials: [ - { - secretText: pass, - endDateTime: expiresOn - } - ] - }; - - let req = new WebResource(); - req.method = "POST"; - req.url = `${graphUrl}/beta/applications`; - req.headers = {}; - req.headers["Content-Type"] = "application/json; charset=utf-8"; - req.headers["accept-language"] = "*"; - req.headers["x-ms-client-request-id"] = GBAdminService.generateUuid(); - req.headers["Authorization"] = "Bearer " + accessToken; - req.body = parameters; - - let httpClient = new ServiceClient(); - return await httpClient.sendRequest(req); - } - /** * @see https://github.com/Azure/azure-rest-api-specs/blob/master/specification/botservice/resource-manager/Microsoft.BotService/preview/2017-12-01/botservice.json */ @@ -478,14 +477,16 @@ export class AzureDeployerService extends GBService { location, nlpAppId, nlpKey, - subscriptionId, - appId + appId, + appPassword, + subscriptionId ) { return new Promise(async (resolve, reject) => { let baseUrl = `https://management.azure.com/`; await this.registerProviders(subscriptionId, baseUrl, accessToken); - let appPassword = GBAdminService.getRndPassword(); + instance.marketplaceId = appId; + instance.marketplacePassword = appPassword; let parameters = { location: location, @@ -499,12 +500,12 @@ export class AzureDeployerService extends GBService { displayName: name, endpoint: endpoint, iconUrl: iconUrl, - luisAppIds: [nlpAppId], - luisKey: nlpKey, + //luisAppIds: [nlpAppId], + //luisKey: nlpKey, msaAppId: appId, msaAppPassword: appPassword, - enabledChannels: ["webchat", "skype", "facebook"], - configuredChannels: ["webchat", "skype", "facebook"] + //enabledChannels: ["webchat"], // , "skype", "facebook"], + configuredChannels: ["webchat"] // , "skype", "facebook"] } }; @@ -524,7 +525,9 @@ export class AzureDeployerService extends GBService { reject(res.bodyAsText); return; } + logger.info(`Bot creation request done waiting for key generation...`); + resolve(instance); setTimeout(async () => { try { @@ -535,18 +538,13 @@ export class AzureDeployerService extends GBService { req = this.createRequestObject( url, accessToken, - "PUT", + "GET", JSON.stringify(parameters) ); let resChannel = await httpClient.sendRequest(req); - - console.log(resChannel.bodyAsText); let key = (resChannel.bodyAsJson as any).properties.properties .sites[0].key; - - instance.marketplacePassword = appPassword; instance.webchatKey = key; - resolve(instance); } catch (error) { reject(error); @@ -561,7 +559,10 @@ export class AzureDeployerService extends GBService { let password = GBConfigService.get("CLOUD_PASSWORD"); let subscriptionId = GBConfigService.get("CLOUD_SUBSCRIPTIONID"); - let accessToken = await GBAdminService.getADALTokenFromUsername(username, password); + let accessToken = await GBAdminService.getADALTokenFromUsername( + username, + password + ); let httpClient = new ServiceClient(); let parameters = { diff --git a/packages/core.gbapp/services/GBCoreService.ts b/packages/core.gbapp/services/GBCoreService.ts index 81c14f54..8d0e8309 100644 --- a/packages/core.gbapp/services/GBCoreService.ts +++ b/packages/core.gbapp/services/GBCoreService.ts @@ -1,8 +1,8 @@ /*****************************************************************************\ | ( )_ _ | -| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ | -| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ | -| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) | +| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ _ _ _ | +| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/ \ /`\ /'_`\ | +| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| |*| |( (_) ) | | | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' | | | | ( )_) | | | (_) \___/' | @@ -300,20 +300,22 @@ export class GBCoreService implements IGBCoreService { public async writeEnv(instance: IGBInstance) { let env = - `ADMIN_PASS=${instance.adminPass}\n` + `ADDITIONAL_DEPLOY_PATH=\n` + + `ADMIN_PASS=${instance.adminPass}\n` + + `CLOUD_SUBSCRIPTIONID=${instance.cloudSubscriptionId}\n` + + `CLOUD_LOCATION=${instance.cloudLocation}\n` + + `CLOUD_GROUP=${instance.botId}\n` + + `CLOUD_USERNAME=${instance.cloudUsername}\n` + + `CLOUD_PASSWORD=${instance.cloudPassword}\n` + + `MSAPP_ID=${instance.marketplaceId}\n`+ + `MSAPP_PASSWORD=${instance.marketplacePassword}\n`+ + `NLP_AUTHORING_KEY=${instance.nlpAuthoringKey}\n`+ `STORAGE_DIALECT=${instance.storageDialect}\n` + `STORAGE_SERVER=${instance.storageServer}.database.windows.net\n` + `STORAGE_NAME=${instance.storageName}\n` + `STORAGE_USERNAME=${instance.storageUsername}\n` + `STORAGE_PASSWORD=${instance.storagePassword}\n` + - `STORAGE_SYNC=true\n` + - `CLOUD_USERNAME=${instance.cloudUsername}\n` + - `CLOUD_PASSWORD=${instance.cloudPassword}\n` + - `CLOUD_SUBSCRIPTIONID=${instance.cloudSubscriptionId}\n` + - `CLOUD_LOCATION=${instance.cloudLocation}\n` + - `CLOUD_GROUP=${instance.botId}\n` + - `NLP_AUTHORING_KEY=${instance.nlpAuthoringKey}`; + `STORAGE_SYNC=true\n`; fs.writeFileSync(".env", env); } diff --git a/src/app.ts b/src/app.ts index a29b1a6c..62983196 100644 --- a/src/app.ts +++ b/src/app.ts @@ -108,7 +108,7 @@ export class GBServer { bootInstance = await azureDeployer.deployFarm(proxyAddress); } catch (error) { logger.warn( - "Error while deploying to the cloud, please, cleanup any objects created before running again." + "In case of error, please cleanup any infrastructure (cloud or on-premises) objects created before running again." ); throw error; } @@ -205,7 +205,7 @@ export class GBServer { // Load all instances from .gbot found on deploy package directory. if (!instances) { let saveInstance = new GuaribasInstance(bootInstance); - saveInstance.save(); + await saveInstance.save(); instances = await core.loadInstances(); }