1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | const async = require('async');
|
17 | const chalk = require('chalk');
|
18 |
|
19 | const {
|
20 | AllCommandsNotRequiringAuth,
|
21 | AuthOptionsNames,
|
22 | CommandRequirement,
|
23 | EnvironmentVariables,
|
24 | Errors,
|
25 | CommonOptionsNames,
|
26 | LogLevel,
|
27 | Namespace,
|
28 | OutputFormat,
|
29 | StderrLogLevels
|
30 | } = require('./Constants');
|
31 | const Authentication = require('./Authentication');
|
32 | const KinveyError = require('./KinveyError');
|
33 | const Request = require('./Request');
|
34 | const { formatHost, getCommandNameFromOptions, isEmpty, isNullOrUndefined, isValidEmail } = require('./Utils');
|
35 |
|
36 | const cmdInit = require('./init/init');
|
37 | const profilesRouter = require('./profile/profilesRouter');
|
38 | const flexRouter = require('./flex/flexRouter');
|
39 | const organizationsRouter = require('./organization/organizationsRouter');
|
40 | const applicationsRouter = require('./application/applicationsRouter');
|
41 | const servicesRouter = require('./service/servicesRouter');
|
42 | const environmentsRouter = require('./environment/environmentsRouter');
|
43 | const collectionsRouter = require('./collection/collectionsRouter');
|
44 | const sitesRouter = require('./website/sitesRouter');
|
45 |
|
46 | const InitController = require('./init/InitController');
|
47 | const ProfilesController = require('./profile/ProfilesController');
|
48 | const FlexController = require('./flex/FlexController');
|
49 | const OrganizationsController = require('./organization/OrganizationsController');
|
50 | const ApplicationsController = require('./application/ApplicationsController');
|
51 | const ServicesController = require('./service/ServicesController');
|
52 | const EnvironmentsController = require('./environment/EnvironmentsController');
|
53 | const CollectionsController = require('./collection/CollectionsController');
|
54 | const SitesController = require('./website/SitesController');
|
55 |
|
56 | const ServicesService = require('./service/ServicesService');
|
57 | const OrganizationsService = require('./organization/OrganizationsService');
|
58 | const ApplicationsService = require('./application/ApplicationsService');
|
59 | const EnvironmentsService = require('./environment/EnvironmentsService');
|
60 | const CollectionsService = require('./collection/CollectionsService');
|
61 | const BusinessLogicService = require('./BusinessLogicService');
|
62 | const PushService = require('./PushService');
|
63 | const RolesService = require('./RolesService');
|
64 | const GroupsService = require('./GroupsService');
|
65 | const SitesService = require('./website/SitesService');
|
66 | const SiteEnvsService = require('./website/SiteEnvsService');
|
67 |
|
68 | const AppFileExporter = require('./exporter/AppFileExporter');
|
69 | const EnvFileExporter = require('./exporter/EnvFileExporterV1');
|
70 | const OrgFileExporter = require('./exporter/OrgFileExporter');
|
71 | const ServiceExporter = require('./exporter/ServiceFileExporter');
|
72 |
|
73 | const AppFileProcessor = require('./AppFileProcessor');
|
74 | const EnvFileProcessor = require('./EnvFileProcessor');
|
75 | const OrgFileProcessor = require('./OrgFileProcessor');
|
76 | const ServiceFileProcessor = require('./ServiceFileProcessor');
|
77 | const ConfigFileProcessor = require('./ConfigFileProcessor');
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | class CLIManager {
|
83 | constructor({ setup, config, logger, notifier, cliVersion, prompter, commandsManager }) {
|
84 | this._setup = setup;
|
85 | this._authentication = new Authentication(this);
|
86 | this._currentProfileName = null;
|
87 | this._isOneTimeSession = false;
|
88 | this._requiresAuth = true;
|
89 | this._outputFormat = OutputFormat.HUMAN_READABLE;
|
90 | this._baasHost = '';
|
91 | this._logger = logger;
|
92 | this._notifier = notifier;
|
93 | this._cliVersion = cliVersion;
|
94 | this._commandsManager = commandsManager;
|
95 | this.config = config;
|
96 | this.prompter = prompter;
|
97 |
|
98 | this._registerControllers();
|
99 | }
|
100 |
|
101 | _registerControllers() {
|
102 | this._controllers = {};
|
103 | this._controllers.init = new InitController({ cliManager: this });
|
104 | const applicationsService = new ApplicationsService(this);
|
105 | const organizationsService = new OrganizationsService(this);
|
106 | const collectionsService = new CollectionsService(this);
|
107 | const environmentsService = new EnvironmentsService(this);
|
108 | const servicesService = new ServicesService(this);
|
109 | const rolesService = new RolesService(this);
|
110 | const groupsService = new GroupsService(this);
|
111 | const businessLogicService = new BusinessLogicService(this);
|
112 | const pushService = new PushService(this);
|
113 | const sitesService = new SitesService(this);
|
114 | const siteEnvsService = new SiteEnvsService(this);
|
115 |
|
116 | const envExporter = new EnvFileExporter({
|
117 | cliManager: this,
|
118 | applicationsService,
|
119 | collectionsService,
|
120 | businessLogicService,
|
121 | rolesService,
|
122 | groupsService,
|
123 | pushService,
|
124 | servicesService
|
125 | });
|
126 | const serviceExporter = new ServiceExporter({ cliManager: this, servicesService });
|
127 | const appExporter = new AppFileExporter({ cliManager: this, applicationsService, servicesService, envExporter, serviceExporter });
|
128 | const orgExporter = new OrgFileExporter({ cliManager: this, organizationsService, applicationsService, servicesService, appExporter, serviceExporter });
|
129 |
|
130 | this._controllers[Namespace.FLEX] = new FlexController({ cliManager: this, applicationsService, organizationsService, servicesService });
|
131 | this._controllers[Namespace.PROFILE] = new ProfilesController({ cliManager: this });
|
132 | this._controllers[Namespace.ORG] = new OrganizationsController({ cliManager: this, organizationsService, exporter: orgExporter });
|
133 | this._controllers[Namespace.APP] = new ApplicationsController({ cliManager: this, exporter: appExporter, applicationsService, organizationsService });
|
134 | this._controllers[Namespace.ENV] = new EnvironmentsController({ cliManager: this, exporter: envExporter, environmentsService });
|
135 | this._controllers[Namespace.SERVICE] = new ServicesController({ cliManager: this, exporter: serviceExporter, applicationsService, organizationsService });
|
136 | this._controllers[Namespace.COLL] = new CollectionsController({ cliManager: this, collectionsService, environmentsService });
|
137 | this._controllers[Namespace.SITE] = new SitesController({ cliManager: this, sitesService, siteEnvsService, organizationsService, applicationsService });
|
138 |
|
139 | const envFileProcessor = new EnvFileProcessor({
|
140 | cliManager: this,
|
141 | applicationsService,
|
142 | environmentsService,
|
143 | collectionsService,
|
144 | businessLogicService,
|
145 | rolesService,
|
146 | groupsService,
|
147 | pushService,
|
148 | servicesService
|
149 | });
|
150 |
|
151 | const serviceFileProcessor = new ServiceFileProcessor({ cliManager: this, servicesService });
|
152 |
|
153 | const appFileProcessor = new AppFileProcessor({ cliManager: this, applicationsService, environmentsService, servicesService, envFileProcessor, serviceFileProcessor });
|
154 | const orgFileProcessor = new OrgFileProcessor({ cliManager: this, organizationsService, servicesService, applicationsService, serviceFileProcessor, appFileProcessor });
|
155 |
|
156 | this._configFileProcessor = new ConfigFileProcessor({ appFileProcessor, envFileProcessor, serviceFileProcessor, orgFileProcessor });
|
157 | }
|
158 |
|
159 | getController(name) {
|
160 | const ctrl = this._controllers[name];
|
161 | if (isNullOrUndefined(ctrl)) {
|
162 | throw new Error(`Controller not found: ${name}.`);
|
163 | }
|
164 |
|
165 | return ctrl;
|
166 | }
|
167 |
|
168 | _processCommonOptions(options) {
|
169 | if (options[CommonOptionsNames.NO_COLOR]) {
|
170 | this._logger.stripColors = true;
|
171 | chalk.level = 0;
|
172 | }
|
173 |
|
174 | if (options[CommonOptionsNames.OUTPUT]) {
|
175 | this._outputFormat = options[CommonOptionsNames.OUTPUT];
|
176 | }
|
177 |
|
178 | if (options[CommonOptionsNames.SILENT] && options[CommonOptionsNames.VERBOSE]) {
|
179 | const errMsg = `Mutually exclusive options: '${CommonOptionsNames.SILENT}' and '${CommonOptionsNames.VERBOSE}'.`;
|
180 | throw new KinveyError(null, errMsg);
|
181 | }
|
182 |
|
183 | if (options[CommonOptionsNames.SILENT]) {
|
184 | this._logger.level = LogLevel.SILENT;
|
185 | }
|
186 |
|
187 | if (options[CommonOptionsNames.VERBOSE]) {
|
188 | this._logger.level = LogLevel.DEBUG;
|
189 | }
|
190 |
|
191 | if (!options[CommonOptionsNames.SUPPRESS_VERSION_CHECK]) {
|
192 | this.log(LogLevel.DEBUG, 'Checking for package updates');
|
193 | this._notifier.notify({ defer: false });
|
194 | }
|
195 | }
|
196 |
|
197 | static _authIsRequired(cmd) {
|
198 | if (AllCommandsNotRequiringAuth.includes(cmd)) {
|
199 | return false;
|
200 | }
|
201 |
|
202 | return true;
|
203 | }
|
204 |
|
205 | _setIsOneTimeSession(options) {
|
206 | const commandName = getCommandNameFromOptions(options);
|
207 | if (AllCommandsNotRequiringAuth.includes(commandName)) {
|
208 | this._isOneTimeSession = false;
|
209 | return;
|
210 | }
|
211 |
|
212 | if (!isNullOrUndefined(options[AuthOptionsNames.PROFILE])) {
|
213 | this._isOneTimeSession = false;
|
214 | return;
|
215 | }
|
216 |
|
217 | this._isOneTimeSession = !isNullOrUndefined(options[AuthOptionsNames.EMAIL])
|
218 | && !isNullOrUndefined(options[AuthOptionsNames.PASSWORD]);
|
219 | }
|
220 |
|
221 | isOneTimeSession() {
|
222 | return this._isOneTimeSession;
|
223 | }
|
224 |
|
225 | static _clearAuthOptions(options) {
|
226 | const commandName = getCommandNameFromOptions(options);
|
227 | const disregardAllAuthOptions = commandName === 'init' || commandName === `${Namespace.PROFILE} login`;
|
228 |
|
229 | if (disregardAllAuthOptions) {
|
230 | Object.keys(AuthOptionsNames).forEach((key) => {
|
231 | const propName = AuthOptionsNames[key];
|
232 | options[propName] = null;
|
233 | });
|
234 |
|
235 | return;
|
236 | }
|
237 |
|
238 | if (commandName === 'profile create') {
|
239 | options[AuthOptionsNames.PROFILE] = null;
|
240 | }
|
241 | }
|
242 |
|
243 | |
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 | _processAuthOptions(options) {
|
250 | const commandName = getCommandNameFromOptions(options);
|
251 | this._requiresAuth = CLIManager._authIsRequired(commandName);
|
252 |
|
253 | CLIManager._clearAuthOptions(options);
|
254 |
|
255 | this._setIsOneTimeSession(options);
|
256 |
|
257 | if (!isNullOrUndefined(options[AuthOptionsNames.BAAS_HOST])) {
|
258 | this._baasHost = options[AuthOptionsNames.BAAS_HOST];
|
259 | }
|
260 |
|
261 | if (!isNullOrUndefined(options[AuthOptionsNames.HOST])) {
|
262 | options[AuthOptionsNames.HOST] = formatHost(options[AuthOptionsNames.HOST]);
|
263 | }
|
264 | }
|
265 |
|
266 | |
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 |
|
273 | _setCurrentUserFromProfile(profileName) {
|
274 | if (this._isOneTimeSession || !this._setup.hasProfiles()) {
|
275 | return;
|
276 | }
|
277 |
|
278 | let profile;
|
279 | if (!isNullOrUndefined(profileName)) {
|
280 | profile = this._setup.findProfileByName(profileName);
|
281 | } else if (this._setup.hasActiveProfile()) {
|
282 | profile = this._setup.getActiveProfile();
|
283 | } else {
|
284 |
|
285 | const allProfiles = this._setup.getProfiles();
|
286 | const isSingleProfile = Object.keys(allProfiles).length === 1;
|
287 | if (isSingleProfile) {
|
288 | const singleProfileName = Object.keys(allProfiles)[0];
|
289 | profile = this._setup.findProfileByName(singleProfileName);
|
290 | }
|
291 | }
|
292 |
|
293 | if (!profile && profileName) {
|
294 | throw new KinveyError(Errors.ProfileNotFound);
|
295 | }
|
296 |
|
297 | if (profile) {
|
298 | this._currentProfileName = profile.name;
|
299 | this.log(LogLevel.DEBUG, `Using profile '${this._currentProfileName}'`);
|
300 | this._authentication.setCurrentUser(profile);
|
301 | }
|
302 | }
|
303 |
|
304 | _loadGlobalSetup() {
|
305 | if (this._isOneTimeSession) {
|
306 | return;
|
307 | }
|
308 |
|
309 | const err = this._setup.load();
|
310 | const canContinue = !err || (err instanceof Error && err.code === 'ENOENT');
|
311 | if (!canContinue) {
|
312 | this.log(LogLevel.DEBUG, 'Failed to load global settings: unable to load config file.');
|
313 | throw err;
|
314 | }
|
315 | }
|
316 |
|
317 | _initCommandsManager(args) {
|
318 |
|
319 | if (!isNullOrUndefined(args)) {
|
320 | this._commandsManager(args);
|
321 | }
|
322 |
|
323 | this._commandsManager
|
324 | .usage('kinvey <command> [args] [options]')
|
325 | .env(EnvironmentVariables.PREFIX)
|
326 | .option(
|
327 | AuthOptionsNames.BAAS_HOST, {
|
328 | global: true,
|
329 | describe: 'Custom BAAS host',
|
330 | type: 'string',
|
331 | hidden: true
|
332 | }
|
333 | )
|
334 | .option(
|
335 | AuthOptionsNames.EMAIL, {
|
336 | global: true,
|
337 | describe: 'E-mail address of your Kinvey account',
|
338 | type: 'string'
|
339 | }
|
340 | )
|
341 | .option(
|
342 | AuthOptionsNames.PASSWORD, {
|
343 | global: true,
|
344 | describe: 'Password of your Kinvey account',
|
345 | type: 'string'
|
346 | }
|
347 | )
|
348 | .option(
|
349 | AuthOptionsNames.TWO_FACTOR_AUTH_TOKEN, {
|
350 | global: true,
|
351 | alias: '2Fa',
|
352 | describe: 'Two-factor authentication token',
|
353 | type: 'string'
|
354 | }
|
355 | )
|
356 | .option(
|
357 | AuthOptionsNames.HOST, {
|
358 | global: true,
|
359 | alias: 'instanceId',
|
360 | describe: 'Instance ID',
|
361 | type: 'string'
|
362 | }
|
363 | )
|
364 | .option(
|
365 | AuthOptionsNames.PROFILE, {
|
366 | global: true,
|
367 | describe: 'Profile to use',
|
368 | type: 'string'
|
369 | }
|
370 | )
|
371 | .option(
|
372 | CommonOptionsNames.OUTPUT, {
|
373 | global: true,
|
374 | describe: 'Output format',
|
375 | type: 'string',
|
376 | choices: [OutputFormat.JSON]
|
377 | }
|
378 | )
|
379 | .option(
|
380 | CommonOptionsNames.SILENT, {
|
381 | global: true,
|
382 | describe: 'Do not output anything',
|
383 | type: 'boolean'
|
384 | }
|
385 | )
|
386 | .option(
|
387 | CommonOptionsNames.SUPPRESS_VERSION_CHECK, {
|
388 | global: true,
|
389 | alias: 'suppressVersionCheck',
|
390 | describe: 'Do not check for package updates',
|
391 | type: 'boolean'
|
392 | }
|
393 | )
|
394 | .option(CommonOptionsNames.VERBOSE, {
|
395 | global: true,
|
396 | describe: 'Output debug messages',
|
397 | type: 'boolean'
|
398 | })
|
399 | .option(CommonOptionsNames.NO_COLOR, {
|
400 | global: true,
|
401 | alias: 'noColor',
|
402 | describe: 'Disable colors',
|
403 | type: 'boolean'
|
404 | })
|
405 | .check((argv) => {
|
406 |
|
407 | this._processCommonOptions(argv);
|
408 | this._processAuthOptions(argv);
|
409 |
|
410 | this._loadGlobalSetup();
|
411 |
|
412 | if (!this._isOneTimeSession && this._requiresAuth) {
|
413 | this._setCurrentUserFromProfile(argv[AuthOptionsNames.PROFILE]);
|
414 | }
|
415 |
|
416 | if (this._requiresAuth && !this._isOneTimeSession && !this._authentication.hasCurrentUser()) {
|
417 | throw new Error('You must be authenticated.');
|
418 | }
|
419 |
|
420 | return true;
|
421 | })
|
422 | .command(cmdInit(this))
|
423 | .command(Namespace.PROFILE, `Manage profiles. Run 'kinvey ${Namespace.PROFILE} -h' for details.`, profilesRouter(this))
|
424 | .command(Namespace.ORG, `Manage organizations. Run 'kinvey ${Namespace.ORG} -h' for details.`, organizationsRouter(this))
|
425 | .command(Namespace.APP, `Manage applications. Run 'kinvey ${Namespace.APP} -h' for details.`, applicationsRouter(this))
|
426 | .command(Namespace.ENV, `Manage environments. Run 'kinvey ${Namespace.ENV} -h' for details.`, environmentsRouter(this))
|
427 | .command(Namespace.COLL, `Manage collections. Run 'kinvey ${Namespace.COLL} -h' for details.`, collectionsRouter(this))
|
428 | .command(Namespace.SERVICE, `Manage services. Run 'kinvey ${Namespace.SERVICE} -h' for details.`, servicesRouter(this))
|
429 | .command(Namespace.FLEX, `Deploy and manage flex services. Run 'kinvey ${Namespace.FLEX} -h' for details.`, flexRouter(this))
|
430 | .command(Namespace.SITE, `Manage websites. Run 'kinvey ${Namespace.SITE} -h' for details.`, sitesRouter(this))
|
431 | .demand(1, '')
|
432 | .strict(true)
|
433 | .help('h')
|
434 | .alias('h', 'help')
|
435 | .showHelpOnFail(true);
|
436 |
|
437 | this._commandsManager.argv;
|
438 | }
|
439 |
|
440 | |
441 |
|
442 |
|
443 |
|
444 |
|
445 |
|
446 |
|
447 |
|
448 | executeCommandHandler(ctrl, commandHandler, requirements = [], options) {
|
449 | async.series([
|
450 | (next) => {
|
451 | const requiresProfile = requirements.includes(CommandRequirement.PROFILE_AVAILABLE);
|
452 | if (!requiresProfile) {
|
453 | return setImmediate(next);
|
454 | }
|
455 |
|
456 | if (!this.getCurrentProfileName()) {
|
457 | return setImmediate(() => { next(new KinveyError(Errors.ProfileRequired)); });
|
458 | }
|
459 |
|
460 | return setImmediate(next);
|
461 | },
|
462 | (next) => {
|
463 | const requiresAuth = requirements.includes(CommandRequirement.AUTH);
|
464 | if (!requiresAuth) {
|
465 | return setImmediate(next);
|
466 | }
|
467 |
|
468 | this.setCurrentUserFromOptions(options, next);
|
469 | },
|
470 | (next) => {
|
471 | ctrl[commandHandler].apply(ctrl, [options, next]);
|
472 | }
|
473 | ], (err, results) => {
|
474 | if (err) {
|
475 | return this.processCommandResult(err);
|
476 | }
|
477 |
|
478 | const result = results.pop();
|
479 | this.processCommandResult(null, result);
|
480 | });
|
481 | }
|
482 |
|
483 | |
484 |
|
485 |
|
486 |
|
487 |
|
488 |
|
489 |
|
490 |
|
491 |
|
492 |
|
493 |
|
494 |
|
495 | applyCommandDefinitions(commandDefinitions, commandsManager, ctrl) {
|
496 | if (isEmpty(commandDefinitions) || !Array.isArray(commandDefinitions)) {
|
497 | throw new Error('Command definitions are either empty or not an array.');
|
498 | }
|
499 |
|
500 | commandDefinitions.forEach((cmdDef) => {
|
501 | const handlerName = cmdDef.handlerName;
|
502 | if (typeof ctrl[handlerName] !== 'function') {
|
503 | throw new Error(`${ctrl.constructor.name} does not contain a function called '${handlerName}'.`);
|
504 | }
|
505 |
|
506 | const modifiedDef = {
|
507 | command: cmdDef.command,
|
508 | desc: cmdDef.desc,
|
509 | handler: (options) => {
|
510 |
|
511 | this.executeCommandHandler(ctrl, handlerName, cmdDef.requirements, options);
|
512 | }
|
513 | };
|
514 |
|
515 | const ensureErrorOnUndeclaredArgs = (commandsManager) => { commandsManager.demandCommand(0, 0); };
|
516 |
|
517 | if (cmdDef.builder) {
|
518 | const originalBuilder = cmdDef.builder;
|
519 | modifiedDef.builder = (commandsManager) => {
|
520 | ensureErrorOnUndeclaredArgs(commandsManager);
|
521 | originalBuilder(commandsManager, this);
|
522 | };
|
523 | } else {
|
524 | modifiedDef.builder = ensureErrorOnUndeclaredArgs;
|
525 | }
|
526 |
|
527 |
|
528 | commandsManager.command(modifiedDef);
|
529 | });
|
530 | }
|
531 |
|
532 | |
533 |
|
534 |
|
535 |
|
536 |
|
537 |
|
538 |
|
539 |
|
540 | _logoutUserFromProfile(profileName, done) {
|
541 | const profile = this._setup.findProfileByName(profileName);
|
542 | if (isNullOrUndefined(profile)) {
|
543 | return setImmediate(done);
|
544 | }
|
545 |
|
546 | this._authentication.setCurrentUser(profile);
|
547 | this._authentication.logout((err) => {
|
548 | if (err) {
|
549 | this.log(LogLevel.DEBUG, `Failed to invalidate token for profile with name '${profileName}'.`);
|
550 | }
|
551 |
|
552 | done();
|
553 | });
|
554 | }
|
555 |
|
556 | |
557 |
|
558 |
|
559 |
|
560 | init(args) {
|
561 | this._initCommandsManager(args);
|
562 | }
|
563 |
|
564 | |
565 |
|
566 |
|
567 |
|
568 |
|
569 |
|
570 |
|
571 |
|
572 |
|
573 |
|
574 |
|
575 |
|
576 |
|
577 | sendRequest(options = {}, done) {
|
578 | if (isNullOrUndefined(options.host)) {
|
579 | options.host = this.config.host;
|
580 | }
|
581 |
|
582 | if (options.isBaas && this._baasHost) {
|
583 | options.baasHost = this._baasHost;
|
584 | }
|
585 |
|
586 | if (isNullOrUndefined(options.timeout)) {
|
587 | options.timeout = this.config.timeout;
|
588 | }
|
589 |
|
590 | options.cliVersion = this._cliVersion;
|
591 |
|
592 | const reqObj = new Request(this._authentication.getCurrentUser(), options);
|
593 |
|
594 | this.log(LogLevel.DEBUG, 'Request: %s %s', reqObj.options.method, reqObj.options.url);
|
595 |
|
596 | return reqObj.send((err, res) => {
|
597 | const status = res.statusCode || '';
|
598 | this.log(LogLevel.DEBUG, 'Response: %s %s %s', reqObj.options.method, reqObj.options.url, status);
|
599 | done(err, res.body);
|
600 | });
|
601 | }
|
602 |
|
603 | |
604 |
|
605 |
|
606 |
|
607 |
|
608 | log(logLevel, ...args) {
|
609 | if (!StderrLogLevels.includes(logLevel)) {
|
610 | return;
|
611 | }
|
612 |
|
613 | this._logger[logLevel](...args);
|
614 | }
|
615 |
|
616 | |
617 |
|
618 |
|
619 |
|
620 |
|
621 |
|
622 | _clearSession(done) {
|
623 | if (!this._isOneTimeSession) {
|
624 | return setImmediate(() => { done(); });
|
625 | }
|
626 |
|
627 |
|
628 | this._authentication.logout(done);
|
629 | }
|
630 |
|
631 | |
632 |
|
633 |
|
634 |
|
635 |
|
636 | processCommandResult(err, result) {
|
637 | this._clearSession(() => {
|
638 | if (err) {
|
639 | this.log(LogLevel.ERROR, err);
|
640 |
|
641 |
|
642 | const isAuthTokenError = err.name === 'InvalidCredentials' && err.message === 'Authorization token invalid or expired.';
|
643 | if (isAuthTokenError) {
|
644 | this.log(LogLevel.INFO, `Run 'kinvey ${Namespace.PROFILE} login' to reauthenticate.`);
|
645 | }
|
646 |
|
647 | return process.exit(1);
|
648 | }
|
649 |
|
650 | const output = result.getFormattedResult(this._outputFormat);
|
651 | this._logger.log(LogLevel.DATA, output);
|
652 | });
|
653 | }
|
654 |
|
655 | |
656 |
|
657 |
|
658 |
|
659 |
|
660 |
|
661 | setCurrentUserFromOptions(options, done) {
|
662 | if (this._authentication.hasCurrentUser()) {
|
663 | return setImmediate(() => { done(); });
|
664 | }
|
665 |
|
666 | this.login(
|
667 | options[AuthOptionsNames.EMAIL],
|
668 | options[AuthOptionsNames.PASSWORD],
|
669 | options[AuthOptionsNames.TWO_FACTOR_AUTH_TOKEN],
|
670 | options[AuthOptionsNames.HOST],
|
671 | done
|
672 | );
|
673 | }
|
674 |
|
675 | |
676 |
|
677 |
|
678 |
|
679 |
|
680 |
|
681 |
|
682 |
|
683 | login(email, password, MFAToken, host = this.config.host, done) {
|
684 | const emailIsValid = isValidEmail(email);
|
685 | if (!emailIsValid) {
|
686 | return setImmediate(() => { done(new KinveyError(Errors.InvalidEmail)); });
|
687 | }
|
688 |
|
689 | this.log(LogLevel.DEBUG, `Logging in user: ${email}`);
|
690 | this._authentication.login(email, password, MFAToken, host, done);
|
691 | }
|
692 |
|
693 | |
694 |
|
695 |
|
696 |
|
697 |
|
698 |
|
699 |
|
700 |
|
701 |
|
702 |
|
703 | createProfile(profileName, email, password, MFAToken, host = this.config.host, done) {
|
704 | if (this.profileExists(profileName)) {
|
705 | this.log(LogLevel.DEBUG, `Overriding profile with name '${profileName}'.`);
|
706 | }
|
707 |
|
708 | if (isNullOrUndefined(profileName)) {
|
709 | return done(new Error('Profile name is not set.'));
|
710 | }
|
711 |
|
712 | let token;
|
713 |
|
714 | async.series([
|
715 | (next) => {
|
716 | this.login(email, password, MFAToken, host, (err, data) => {
|
717 | if (err) {
|
718 | return next(err);
|
719 | }
|
720 |
|
721 | token = data.token;
|
722 | next();
|
723 | });
|
724 | },
|
725 | (next) => {
|
726 | const newUser = this._authentication.getCurrentUser();
|
727 | this._logoutUserFromProfile(profileName, () => {
|
728 | this._authentication.setCurrentUser(newUser);
|
729 | next();
|
730 | });
|
731 | },
|
732 | (next) => {
|
733 |
|
734 | this._setup.addProfile(profileName, email, token, host);
|
735 | this._setup.save(next);
|
736 | }
|
737 | ], done);
|
738 | }
|
739 |
|
740 | reAuthenticateProfile(profileName, password, mfaToken, done) {
|
741 | const profile = this.findProfile(profileName);
|
742 | if (isNullOrUndefined(profile)) {
|
743 | return setImmediate(() => done(new KinveyError(Errors.ProfileNotFound)));
|
744 | }
|
745 |
|
746 | this.log(LogLevel.DEBUG, `Re-authenticating profile with name '${profileName}'.`);
|
747 |
|
748 | async.waterfall([
|
749 | (next) => {
|
750 | this.login(profile.email, password, mfaToken, profile.host, (err, data) => {
|
751 | if (err) {
|
752 | return next(err);
|
753 | }
|
754 |
|
755 | const token = data.token;
|
756 | next(null, token);
|
757 | });
|
758 | },
|
759 | (token, next) => {
|
760 | this._setup.setProfileToken(profileName, token);
|
761 | this._setup.save(next);
|
762 | }
|
763 | ], done);
|
764 | }
|
765 |
|
766 | |
767 |
|
768 |
|
769 |
|
770 | getProfiles() {
|
771 | return this._setup.getProfiles();
|
772 | }
|
773 |
|
774 | |
775 |
|
776 |
|
777 |
|
778 |
|
779 | findProfile(name) {
|
780 | return this._setup.findProfileByName(name);
|
781 | }
|
782 |
|
783 | |
784 |
|
785 |
|
786 |
|
787 |
|
788 | profileExists(name) {
|
789 | return isNullOrUndefined(this._setup.findProfileByName(name)) === false;
|
790 | }
|
791 |
|
792 | |
793 |
|
794 |
|
795 |
|
796 |
|
797 | deleteProfile(name, done) {
|
798 | async.series([
|
799 | (next) => {
|
800 | this._logoutUserFromProfile(name, next);
|
801 | },
|
802 | (next) => {
|
803 | this._setup.deleteProfile(name);
|
804 | this._setup.save(next);
|
805 | }
|
806 | ], done);
|
807 | }
|
808 |
|
809 | getActiveProfile() {
|
810 | return this._setup.getActiveProfile();
|
811 | }
|
812 |
|
813 | setActiveProfile(name, done) {
|
814 | if (!this.profileExists(name)) {
|
815 | return done(new KinveyError(Errors.ProfileNotFound));
|
816 | }
|
817 |
|
818 | this._setup.setActiveProfile(name);
|
819 | this._setup.save(done);
|
820 | }
|
821 |
|
822 | getCurrentProfileName() {
|
823 | return this._currentProfileName;
|
824 | }
|
825 |
|
826 | |
827 |
|
828 |
|
829 |
|
830 |
|
831 | getActiveItem(itemType) {
|
832 | const profileName = this.getCurrentProfileName();
|
833 | if (isNullOrUndefined(profileName)) {
|
834 | return null;
|
835 | }
|
836 |
|
837 | return this._setup.getActiveItemProfileLevel(itemType, profileName);
|
838 | }
|
839 |
|
840 | getActiveItemId(itemType) {
|
841 | const activeItem = this.getActiveItem(itemType);
|
842 | if (!activeItem || isNullOrUndefined(activeItem.id)) {
|
843 | return null;
|
844 | }
|
845 |
|
846 | return activeItem.id;
|
847 | }
|
848 |
|
849 | |
850 |
|
851 |
|
852 |
|
853 |
|
854 |
|
855 | setActiveItem(itemType, item, done) {
|
856 | const profileName = this.getCurrentProfileName();
|
857 | this._setup.setActiveItemProfileLevel(itemType, item, profileName);
|
858 | this._setup.save(done);
|
859 | }
|
860 |
|
861 | |
862 |
|
863 |
|
864 |
|
865 |
|
866 |
|
867 |
|
868 | removeActiveItem(itemType, removedId, done) {
|
869 | if (this.isOneTimeSession()) {
|
870 | return setImmediate(done);
|
871 | }
|
872 |
|
873 | const currentActiveItem = this.getActiveItem(itemType);
|
874 | if (isNullOrUndefined(currentActiveItem) || isEmpty(currentActiveItem)) {
|
875 | return setImmediate(done);
|
876 | }
|
877 |
|
878 | const activeId = currentActiveItem.id;
|
879 | if (removedId !== activeId) {
|
880 | return setImmediate(done);
|
881 | }
|
882 |
|
883 | this.setActiveItem(itemType, { id: null }, (err) => {
|
884 | if (err) {
|
885 | this.log(LogLevel.WARN, `Failed to remove active ${itemType}: ${activeId}. ${err}`);
|
886 | } else {
|
887 | this.log(LogLevel.DEBUG, `Removed active ${itemType}: ${activeId}.`);
|
888 | }
|
889 |
|
890 | done(err);
|
891 | });
|
892 | }
|
893 |
|
894 | processConfigFile(options, done) {
|
895 | this._configFileProcessor.process(options, done);
|
896 | }
|
897 | }
|
898 |
|
899 | module.exports = CLIManager;
|