1
0
mirror of https://github.com/bitwarden/directory-connector synced 2026-01-06 10:33:46 +00:00

stub out cli for directory connector

This commit is contained in:
Kyle Spearrin
2019-03-15 22:38:02 -04:00
parent 7da368d555
commit 59ade0d2ba
10 changed files with 869 additions and 53 deletions

130
src/bwdc.ts Normal file
View File

@@ -0,0 +1,130 @@
import * as fs from 'fs';
import * as path from 'path';
import { AuthService } from 'jslib/services/auth.service';
import { ConfigurationService } from './services/configuration.service';
import { I18nService } from './services/i18n.service';
import { KeytarSecureStorageService } from './services/keytarSecureStorage.service';
import { SyncService } from './services/sync.service';
import { CliPlatformUtilsService } from 'jslib/cli/services/cliPlatformUtils.service';
import { ConsoleLogService } from 'jslib/cli/services/consoleLog.service';
import { AppIdService } from 'jslib/services/appId.service';
import { ConstantsService } from 'jslib/services/constants.service';
import { ContainerService } from 'jslib/services/container.service';
import { CryptoService } from 'jslib/services/crypto.service';
import { EnvironmentService } from 'jslib/services/environment.service';
import { LowdbStorageService } from 'jslib/services/lowdbStorage.service';
import { NodeApiService } from 'jslib/services/nodeApi.service';
import { NodeCryptoFunctionService } from 'jslib/services/nodeCryptoFunction.service';
import { NoopMessagingService } from 'jslib/services/noopMessaging.service';
import { TokenService } from 'jslib/services/token.service';
import { UserService } from 'jslib/services/user.service';
import { Program } from './program';
// tslint:disable-next-line
const packageJson = require('./package.json');
export class Main {
logService: ConsoleLogService;
messagingService: NoopMessagingService;
storageService: LowdbStorageService;
secureStorageService: KeytarSecureStorageService;
i18nService: I18nService;
platformUtilsService: CliPlatformUtilsService;
constantsService: ConstantsService;
cryptoService: CryptoService;
tokenService: TokenService;
appIdService: AppIdService;
apiService: NodeApiService;
environmentService: EnvironmentService;
userService: UserService;
containerService: ContainerService;
cryptoFunctionService: NodeCryptoFunctionService;
authService: AuthService;
configurationService: ConfigurationService;
syncService: SyncService;
program: Program;
constructor() {
const applicationName = 'Bitwarden Directory Connector';
let p = null;
if (process.env.BITWARDENCLI_CONNECTOR_APPDATA_DIR) {
p = path.resolve(process.env.BITWARDENCLI_CONNECTOR_APPDATA_DIR);
} else if (process.env.BITWARDEN_CONNECTOR_APPDATA_DIR) {
p = path.resolve(process.env.BITWARDEN_CONNECTOR_APPDATA_DIR);
} else if (fs.existsSync(path.join(__dirname, 'bitwarden-connector-appdata'))) {
p = path.join(__dirname, 'bitwarden-connector-appdata');
} else if (process.platform === 'darwin') {
p = path.join(process.env.HOME, 'Library/Application Support/' + applicationName);
} else if (process.platform === 'win32') {
p = path.join(process.env.APPDATA, applicationName);
} else if (process.env.XDG_CONFIG_HOME) {
p = path.join(process.env.XDG_CONFIG_HOME, applicationName);
} else {
p = path.join(process.env.HOME, '.config/' + applicationName);
}
this.i18nService = new I18nService('en', './locales');
this.platformUtilsService = new CliPlatformUtilsService('connector', packageJson);
this.logService = new ConsoleLogService(this.platformUtilsService.isDev());
this.cryptoFunctionService = new NodeCryptoFunctionService();
this.storageService = new LowdbStorageService(null, p, true);
this.secureStorageService = new KeytarSecureStorageService(applicationName);
this.cryptoService = new CryptoService(this.storageService, this.secureStorageService,
this.cryptoFunctionService);
this.appIdService = new AppIdService(this.storageService);
this.tokenService = new TokenService(this.storageService);
this.messagingService = new NoopMessagingService();
this.apiService = new NodeApiService(this.tokenService, this.platformUtilsService,
async (expired: boolean) => await this.logout());
this.environmentService = new EnvironmentService(this.apiService, this.storageService, null);
this.userService = new UserService(this.tokenService, this.storageService);
this.containerService = new ContainerService(this.cryptoService);
this.authService = new AuthService(this.cryptoService, this.apiService, this.userService, this.tokenService,
this.appIdService, this.i18nService, this.platformUtilsService, this.messagingService, true);
this.configurationService = new ConfigurationService(this.storageService, this.secureStorageService);
this.syncService = new SyncService(this.configurationService, this.logService, this.cryptoFunctionService,
this.apiService, this.messagingService, this.i18nService);
this.program = new Program(this);
}
async run() {
await this.init();
this.program.run();
}
async logout() {
await Promise.all([
this.tokenService.clearToken(),
this.userService.clear(),
]);
}
private async init() {
this.storageService.init();
this.containerService.attachToWindow(global);
await this.environmentService.setUrlsFromStorage();
// Dev Server URLs. Comment out the line above.
// this.apiService.setUrls({
// base: null,
// api: 'http://localhost:4000',
// identity: 'http://localhost:33656',
// });
const locale = await this.storageService.get<string>(ConstantsService.localeKey);
await this.i18nService.init(locale);
this.authService.init();
const installedVersion = await this.storageService.get<string>(ConstantsService.installedVersionKey);
const currentVersion = this.platformUtilsService.getApplicationVersion();
if (installedVersion == null || installedVersion !== currentVersion) {
await this.storageService.save(ConstantsService.installedVersionKey, currentVersion);
}
}
}
const main = new Main();
main.run();

View File

@@ -0,0 +1,31 @@
import * as program from 'commander';
import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { Response } from 'jslib/cli/models/response';
import { MessageResponse } from 'jslib/cli/models/response/messageResponse';
export class ConfigCommand {
constructor(private environmentService: EnvironmentService) { }
async run(setting: string, value: string, cmd: program.Command): Promise<Response> {
setting = setting.toLowerCase();
switch (setting) {
case 'server':
await this.setServer(value);
break;
default:
return Response.badRequest('Unknown setting.');
}
const res = new MessageResponse('Saved setting `' + setting + '`.', null);
return Response.success(res);
}
private async setServer(url: string) {
url = (url === 'null' || url === 'bitwarden.com' || url === 'https://bitwarden.com' ? null : url);
await this.environmentService.setUrls({
base: url,
});
}
}

212
src/program.ts Normal file
View File

@@ -0,0 +1,212 @@
import * as chk from 'chalk';
import * as program from 'commander';
import { Main } from './bwdc';
import { ConfigCommand } from './commands/config.command';
import { UpdateCommand } from 'jslib/cli/commands/update.command';
import { Response } from 'jslib/cli/models/response';
import { ListResponse } from 'jslib/cli/models/response/listResponse';
import { MessageResponse } from 'jslib/cli/models/response/messageResponse';
import { StringResponse } from 'jslib/cli/models/response/stringResponse';
const chalk = chk.default;
const writeLn = (s: string, finalLine: boolean = false) => {
if (finalLine && process.platform === 'win32') {
process.stdout.write(s);
} else {
process.stdout.write(s + '\n');
}
};
export class Program {
constructor(private main: Main) { }
run() {
program
.option('--pretty', 'Format output. JSON is tabbed with two spaces.')
.option('--raw', 'Return raw output instead of a descriptive message.')
.option('--response', 'Return a JSON formatted version of response output.')
.option('--quiet', 'Don\'t return anything to stdout.')
.version(this.main.platformUtilsService.getApplicationVersion(), '-v, --version');
program.on('option:pretty', () => {
process.env.BW_PRETTY = 'true';
});
program.on('option:raw', () => {
process.env.BW_RAW = 'true';
});
program.on('option:quiet', () => {
process.env.BW_QUIET = 'true';
});
program.on('option:response', () => {
process.env.BW_RESPONSE = 'true';
});
program.on('command:*', () => {
writeLn(chalk.redBright('Invalid command: ' + program.args.join(' ')));
writeLn('See --help for a list of available commands.', true);
process.exitCode = 1;
});
program.on('--help', () => {
writeLn('\n Examples:');
writeLn('');
writeLn(' bwdc login');
writeLn('', true);
});
program
.command('config <setting> <value>')
.description('Configure CLI settings.')
.on('--help', () => {
writeLn('\n Settings:');
writeLn('');
writeLn(' server - On-premise hosted installation URL.');
writeLn('');
writeLn(' Examples:');
writeLn('');
writeLn(' bwdc config server https://bw.company.com');
writeLn(' bwdc config server bitwarden.com');
writeLn('', true);
})
.action(async (setting, value, cmd) => {
const command = new ConfigCommand(this.main.environmentService);
const response = await command.run(setting, value, cmd);
this.processResponse(response);
});
program
.command('update')
.description('Check for updates.')
.on('--help', () => {
writeLn('\n Notes:');
writeLn('');
writeLn(' Returns the URL to download the newest version of this CLI tool.');
writeLn('');
writeLn(' Use the `--raw` option to return only the download URL for the update.');
writeLn('');
writeLn(' Examples:');
writeLn('');
writeLn(' bwdc update');
writeLn(' bwdc update --raw');
writeLn('', true);
})
.action(async (cmd) => {
const command = new UpdateCommand(this.main.platformUtilsService, 'directory-connector', 'bwdc');
const response = await command.run(cmd);
this.processResponse(response);
});
program
.parse(process.argv);
if (process.argv.slice(2).length === 0) {
program.outputHelp();
}
}
private processResponse(response: Response, exitImmediately = false) {
if (!response.success) {
if (process.env.BW_QUIET !== 'true') {
if (process.env.BW_RESPONSE === 'true') {
writeLn(this.getJson(response), true);
} else {
writeLn(chalk.redBright(response.message), true);
}
}
if (exitImmediately) {
process.exit(1);
} else {
process.exitCode = 1;
}
return;
}
if (process.env.BW_RESPONSE === 'true') {
writeLn(this.getJson(response), true);
} else if (response.data != null) {
let out: string = null;
if (response.data.object === 'string') {
const data = (response.data as StringResponse).data;
if (data != null) {
out = data;
}
} else if (response.data.object === 'list') {
out = this.getJson((response.data as ListResponse).data);
} else if (response.data.object === 'message') {
out = this.getMessage(response);
} else {
out = this.getJson(response.data);
}
if (out != null && process.env.BW_QUIET !== 'true') {
writeLn(out, true);
}
}
if (exitImmediately) {
process.exit(0);
} else {
process.exitCode = 0;
}
}
private getJson(obj: any): string {
if (process.env.BW_PRETTY === 'true') {
return JSON.stringify(obj, null, ' ');
} else {
return JSON.stringify(obj);
}
}
private getMessage(response: Response) {
const message = (response.data as MessageResponse);
if (process.env.BW_RAW === 'true' && message.raw != null) {
return message.raw;
}
let out: string = '';
if (message.title != null) {
if (message.noColor) {
out = message.title;
} else {
out = chalk.greenBright(message.title);
}
}
if (message.message != null) {
if (message.title != null) {
out += '\n';
}
out += message.message;
}
return out.trim() === '' ? null : out;
}
private async exitIfLocked() {
await this.exitIfNotAuthed();
const hasKey = await this.main.cryptoService.hasKey();
if (!hasKey) {
this.processResponse(Response.error('Vault is locked.'), true);
}
}
private async exitIfAuthed() {
const authed = await this.main.userService.isAuthenticated();
if (authed) {
const email = await this.main.userService.getEmail();
this.processResponse(Response.error('You are already logged in as ' + email + '.'), true);
}
}
private async exitIfNotAuthed() {
const authed = await this.main.userService.isAuthenticated();
if (!authed) {
this.processResponse(Response.error('You are not logged in.'), true);
}
}
}

View File

@@ -0,0 +1,25 @@
import {
deletePassword,
getPassword,
setPassword,
} from 'keytar';
import { StorageService } from 'jslib/abstractions/storage.service';
export class KeytarSecureStorageService implements StorageService {
constructor(private serviceName: string) { }
get<T>(key: string): Promise<T> {
return getPassword(this.serviceName, key).then((val: any) => {
return val as T;
});
}
save(key: string, obj: any): Promise<any> {
return setPassword(this.serviceName, key, obj);
}
remove(key: string): Promise<any> {
return deletePassword(this.serviceName, key);
}
}