UNPKG

azure-cli

Version:

Microsoft Azure Cross Platform Command Line tool

219 lines (197 loc) 8.1 kB
/** * Copyright (c) Microsoft. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var util = require('util'); var async = require('async'); var __ = require('underscore'); var constants = require('../constants.js'); var Subscription = require('./subscription.js'); var utils = require('../utils'); function Account(env, adalAuth, resourceClient, log) { this._env = env; this._adalAuth = adalAuth; this._resourceClient = resourceClient; this._log = log; this.WarnToUserInteractiveFieldName = 'warnToUserInteractive'; } Account.prototype.load = function (username, password, tenant, loginType, callback) { var self = this; if (!tenant) { tenant = constants.AAD_COMMON_TENANT; } if (username) { username = self._adalAuth.normalizeUserName(username); } self._username = username; var userType = 'user'; if (loginType.servicePrincipal) { userType = 'servicePrincipal'; } function processAndReturnSubscriptions(err, result) { if (err) return callback(err); var subscriptions = result.subscriptions; var subs = __.map(subscriptions, function (s) { return new Subscription({ id: s.subscriptionId, name: s.displayName || s.subscriptionName, user: { name: self._username, type: userType }, state: s.state, tenantId: s.tenantId ? s.tenantId : tenant }, self._env); }); callback(null, { subscriptions: subs, tenantIds: result.tenantIds }); } if (loginType.servicePrincipal) { this._adalAuth.createServicePrincipalTokenCredentials(self._env.getAuthConfig(tenant), username, password, function (err, credential) { if (err) { return callback(err); } self._getSubscriptionsFromTenants(username, [tenant], credential, function (err, subscriptions) { if (err) return callback(err); return processAndReturnSubscriptions(null, { subscriptions: subscriptions }); }); }); } else if (loginType.interactive) { self._getSubscriptionsInteractive(tenant, processAndReturnSubscriptions); } else { self._getSubscriptionsNonInteractive(username, password, tenant, processAndReturnSubscriptions); } }; Account.prototype._getSubscriptionsInteractive = function (tenant, callback) { var self = this; var authConfig = self._env.getAuthConfig(tenant); async.waterfall([ function (callback) { self._adalAuth.acquireUserCode(authConfig, function (err, userCodeResponse) { if (err) return callback(err); return callback(null, userCodeResponse); }); }, function (userCodeResponse, callback) { self._log.info(userCodeResponse.message); self._log.verbose('code response from AAD is :' + JSON.stringify(userCodeResponse)); self._adalAuth.authenticateWithDeviceCode(authConfig, userCodeResponse, function (err, credential) { if (err) return callback(err); self._username = credential.userId; async.waterfall([ function (callback) { self._buildTenantList(self._username, tenant, credential, callback); }, function (tenantList, callback) { self._getSubscriptionsFromTenants(self._username, tenantList, null, callback); }, ], function (err, subscriptions) { callback(err, { subscriptions : subscriptions }); }); }); } ], function (err, subscriptions) { callback(err, subscriptions); }); }; Account.prototype._getSubscriptionsNonInteractive = function (username, password, tenant, callback) { var self = this; self._adalAuth.authenticateWithUsernamePassword(self._env.getAuthConfig(tenant), username, password, function (err, credential) { if (err && err.message && self._errorSuggestsInteractiveFlow(err.message)) { err[self.WarnToUserInteractiveFieldName] = true; } if (err) return callback(err); username = _crossCheckUserNameWithToken(username, credential.userId); async.waterfall([ function (callback) { self._buildTenantList(username, tenant, credential, callback); }, function (tenantList, callback) { self._getSubscriptionsFromTenants(username, tenantList, null, callback); }, ], function (err, subscriptions) { callback(err, { subscriptions : subscriptions }); }); }); }; Account.prototype._errorSuggestsInteractiveFlow = function (errMessage) { var codes = ['50072', '50074', '50076', '50077', '50078', '50079']; var contains2FAErrCode = __.any(codes, function (code) { return (errMessage.indexOf('AADSTS' + code) !== -1); }); var containsErrCausedByUsingMsa = utils.ignoreCaseEquals(errMessage, 'Server returned an unknown AccountType: undefined') || utils.ignoreCaseEquals(errMessage, 'Server returned error in RSTR - ErrorCode: NONE : FaultMessage: NONE'); return contains2FAErrCode || containsErrCausedByUsingMsa; }; Account.prototype._polishError = function (errMessage) { if (utils.ignoreCaseEquals(errMessage, 'Server returned an unknown AccountType: undefined') || utils.ignoreCaseEquals(errMessage, 'Server returned error in RSTR - ErrorCode: NONE : FaultMessage: NONE')) { errMessage = 'Your account type requires to login using interactive flow. Please rerun "login" command without -u/--username parameter'; } return errMessage; }; Account.prototype._getSubscriptionsFromTenants = function (username, tenantList, preBuiltCredential, callback) { var self = this; var subscriptions = []; async.eachSeries(tenantList, function (tenant, cb) { var credential = preBuiltCredential; if (!credential) { credential = new self._adalAuth.UserTokenCredentials(self._env.getAuthConfig(tenant), username); } var armClient = self._getArmClient(credential); armClient.subscriptions.list(function (err, result) { if (!err) { subscriptions = subscriptions.concat(result.map(function (s) { s.tenantId = tenant; s.username = username; return s; })); } cb(err); }); }, function (err) { callback(err, subscriptions); }); }; Account.prototype._buildTenantList = function (username, tenant, credential, callback) { var self = this; var tenants = []; if (tenant && tenant !== constants.AAD_COMMON_TENANT) { return callback(null, [tenant]); } self._getAllTenants(credential, function (err, result) { if (err) return callback(err); async.eachSeries(result, function (tenantInfo, cb) { tenants.push(tenantInfo.tenantId); cb(err); }, function (err) { callback(err, tenants); }); }); }; Account.prototype._getAllTenants = function (credentials, callback) { var armClient = this._getArmClient(credentials); armClient.tenants.list(callback); }; Account.prototype._getArmClient = function (credentials) { return new this._resourceClient.SubscriptionClient(credentials, this._env.resourceManagerEndpointUrl); }; module.exports = Account; function _crossCheckUserNameWithToken(usernameFromCommandline, userIdFromToken) { //to maintain the casing consistency between 'azureprofile.json' and token cache. (RD 1996587) //use the 'userId' here, which should be the same with "username" except the casing. if (utils.ignoreCaseEquals(usernameFromCommandline, userIdFromToken)) { return userIdFromToken; } else { throw new Error(util.format('The userId of \'%s\' in access token doesn\'t match the command line username of \'%s\'', userIdFromToken, usernameFromCommandline)); } }