mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-21 11:35:34 +01:00
[AC-2631] Add device-approval command scaffolding (#9351)
* Add device-approval scaffolding * Refactor: move helpers to BaseProgram * Update CODEOWNERS
This commit is contained in:
parent
89d7e96b25
commit
c0bb7b9edf
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -52,6 +52,7 @@ apps/cli/src/admin-console @bitwarden/team-admin-console-dev
|
|||||||
apps/desktop/src/admin-console @bitwarden/team-admin-console-dev
|
apps/desktop/src/admin-console @bitwarden/team-admin-console-dev
|
||||||
apps/web/src/app/admin-console @bitwarden/team-admin-console-dev
|
apps/web/src/app/admin-console @bitwarden/team-admin-console-dev
|
||||||
bitwarden_license/bit-web/src/app/admin-console @bitwarden/team-admin-console-dev
|
bitwarden_license/bit-web/src/app/admin-console @bitwarden/team-admin-console-dev
|
||||||
|
bitwarden_license/bit-cli/src/admin-console @bitwarden/team-admin-console-dev
|
||||||
libs/angular/src/admin-console @bitwarden/team-admin-console-dev
|
libs/angular/src/admin-console @bitwarden/team-admin-console-dev
|
||||||
libs/common/src/admin-console @bitwarden/team-admin-console-dev
|
libs/common/src/admin-console @bitwarden/team-admin-console-dev
|
||||||
libs/admin-console @bitwarden/team-admin-console-dev
|
libs/admin-console @bitwarden/team-admin-console-dev
|
||||||
|
173
apps/cli/src/base-program.ts
Normal file
173
apps/cli/src/base-program.ts
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
import * as chalk from "chalk";
|
||||||
|
import { firstValueFrom, map } from "rxjs";
|
||||||
|
|
||||||
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
|
||||||
|
import { UnlockCommand } from "./auth/commands/unlock.command";
|
||||||
|
import { Response } from "./models/response";
|
||||||
|
import { ListResponse } from "./models/response/list.response";
|
||||||
|
import { MessageResponse } from "./models/response/message.response";
|
||||||
|
import { StringResponse } from "./models/response/string.response";
|
||||||
|
import { TemplateResponse } from "./models/response/template.response";
|
||||||
|
import { ServiceContainer } from "./service-container";
|
||||||
|
import { CliUtils } from "./utils";
|
||||||
|
|
||||||
|
const writeLn = CliUtils.writeLn;
|
||||||
|
|
||||||
|
export abstract class BaseProgram {
|
||||||
|
constructor(protected serviceContainer: ServiceContainer) {}
|
||||||
|
|
||||||
|
protected 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, false);
|
||||||
|
} else {
|
||||||
|
writeLn(chalk.redBright(response.message), true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const exitCode = process.env.BW_CLEANEXIT ? 0 : 1;
|
||||||
|
if (exitImmediately) {
|
||||||
|
process.exit(exitCode);
|
||||||
|
} else {
|
||||||
|
process.exitCode = exitCode;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.BW_RESPONSE === "true") {
|
||||||
|
writeLn(this.getJson(response), true, false);
|
||||||
|
} else if (response.data != null) {
|
||||||
|
let out: string = null;
|
||||||
|
|
||||||
|
if (response.data.object === "template") {
|
||||||
|
out = this.getJson((response.data as TemplateResponse).template);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out == 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, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getMessage(response: Response): string {
|
||||||
|
const message = response.data as MessageResponse;
|
||||||
|
if (process.env.BW_RAW === "true") {
|
||||||
|
return message.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
let out = "";
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async exitIfAuthed() {
|
||||||
|
const authed = await firstValueFrom(
|
||||||
|
this.serviceContainer.authService.activeAccountStatus$.pipe(
|
||||||
|
map((status) => status > AuthenticationStatus.LoggedOut),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (authed) {
|
||||||
|
const email = await firstValueFrom(
|
||||||
|
this.serviceContainer.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||||
|
);
|
||||||
|
this.processResponse(Response.error("You are already logged in as " + email + "."), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async exitIfNotAuthed() {
|
||||||
|
const authed = await this.serviceContainer.stateService.getIsAuthenticated();
|
||||||
|
if (!authed) {
|
||||||
|
this.processResponse(Response.error("You are not logged in."), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async exitIfLocked() {
|
||||||
|
await this.exitIfNotAuthed();
|
||||||
|
if (await this.serviceContainer.cryptoService.hasUserKey()) {
|
||||||
|
return;
|
||||||
|
} else if (process.env.BW_NOINTERACTION !== "true") {
|
||||||
|
// must unlock
|
||||||
|
if (await this.serviceContainer.keyConnectorService.getUsesKeyConnector()) {
|
||||||
|
const response = Response.error(
|
||||||
|
"Your vault is locked. You must unlock your vault using your session key.\n" +
|
||||||
|
"If you do not have your session key, you can get a new one by logging out and logging in again.",
|
||||||
|
);
|
||||||
|
this.processResponse(response, true);
|
||||||
|
} else {
|
||||||
|
const command = new UnlockCommand(
|
||||||
|
this.serviceContainer.accountService,
|
||||||
|
this.serviceContainer.masterPasswordService,
|
||||||
|
this.serviceContainer.cryptoService,
|
||||||
|
this.serviceContainer.stateService,
|
||||||
|
this.serviceContainer.cryptoFunctionService,
|
||||||
|
this.serviceContainer.apiService,
|
||||||
|
this.serviceContainer.logService,
|
||||||
|
this.serviceContainer.keyConnectorService,
|
||||||
|
this.serviceContainer.environmentService,
|
||||||
|
this.serviceContainer.syncService,
|
||||||
|
this.serviceContainer.organizationApiService,
|
||||||
|
this.serviceContainer.logout,
|
||||||
|
this.serviceContainer.kdfConfigService,
|
||||||
|
);
|
||||||
|
const response = await command.run(null, null);
|
||||||
|
if (!response.success) {
|
||||||
|
this.processResponse(response, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.processResponse(Response.error("Vault is locked."), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async exitIfFeatureFlagDisabled(featureFlag: FeatureFlag) {
|
||||||
|
const enabled = await firstValueFrom(
|
||||||
|
this.serviceContainer.configService.getFeatureFlag$(featureFlag),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
this.processResponse(Response.error("This command is temporarily unavailable."), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import * as chalk from "chalk";
|
import * as chalk from "chalk";
|
||||||
import { program, Command, OptionValues } from "commander";
|
import { program, Command, OptionValues } from "commander";
|
||||||
import { firstValueFrom, map } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
|
|
||||||
@ -8,6 +8,7 @@ import { LockCommand } from "./auth/commands/lock.command";
|
|||||||
import { LoginCommand } from "./auth/commands/login.command";
|
import { LoginCommand } from "./auth/commands/login.command";
|
||||||
import { LogoutCommand } from "./auth/commands/logout.command";
|
import { LogoutCommand } from "./auth/commands/logout.command";
|
||||||
import { UnlockCommand } from "./auth/commands/unlock.command";
|
import { UnlockCommand } from "./auth/commands/unlock.command";
|
||||||
|
import { BaseProgram } from "./base-program";
|
||||||
import { CompletionCommand } from "./commands/completion.command";
|
import { CompletionCommand } from "./commands/completion.command";
|
||||||
import { ConfigCommand } from "./commands/config.command";
|
import { ConfigCommand } from "./commands/config.command";
|
||||||
import { EncodeCommand } from "./commands/encode.command";
|
import { EncodeCommand } from "./commands/encode.command";
|
||||||
@ -15,20 +16,14 @@ import { ServeCommand } from "./commands/serve.command";
|
|||||||
import { StatusCommand } from "./commands/status.command";
|
import { StatusCommand } from "./commands/status.command";
|
||||||
import { UpdateCommand } from "./commands/update.command";
|
import { UpdateCommand } from "./commands/update.command";
|
||||||
import { Response } from "./models/response";
|
import { Response } from "./models/response";
|
||||||
import { ListResponse } from "./models/response/list.response";
|
|
||||||
import { MessageResponse } from "./models/response/message.response";
|
import { MessageResponse } from "./models/response/message.response";
|
||||||
import { StringResponse } from "./models/response/string.response";
|
|
||||||
import { TemplateResponse } from "./models/response/template.response";
|
|
||||||
import { ServiceContainer } from "./service-container";
|
|
||||||
import { GenerateCommand } from "./tools/generate.command";
|
import { GenerateCommand } from "./tools/generate.command";
|
||||||
import { CliUtils } from "./utils";
|
import { CliUtils } from "./utils";
|
||||||
import { SyncCommand } from "./vault/sync.command";
|
import { SyncCommand } from "./vault/sync.command";
|
||||||
|
|
||||||
const writeLn = CliUtils.writeLn;
|
const writeLn = CliUtils.writeLn;
|
||||||
|
|
||||||
export class Program {
|
export class Program extends BaseProgram {
|
||||||
constructor(protected serviceContainer: ServiceContainer) {}
|
|
||||||
|
|
||||||
async register() {
|
async register() {
|
||||||
program
|
program
|
||||||
.option("--pretty", "Format output. JSON is tabbed with two spaces.")
|
.option("--pretty", "Format output. JSON is tabbed with two spaces.")
|
||||||
@ -517,147 +512,4 @@ export class Program {
|
|||||||
await command.run(cmd);
|
await command.run(cmd);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected 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, false);
|
|
||||||
} else {
|
|
||||||
writeLn(chalk.redBright(response.message), true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const exitCode = process.env.BW_CLEANEXIT ? 0 : 1;
|
|
||||||
if (exitImmediately) {
|
|
||||||
process.exit(exitCode);
|
|
||||||
} else {
|
|
||||||
process.exitCode = exitCode;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.BW_RESPONSE === "true") {
|
|
||||||
writeLn(this.getJson(response), true, false);
|
|
||||||
} else if (response.data != null) {
|
|
||||||
let out: string = null;
|
|
||||||
|
|
||||||
if (response.data.object === "template") {
|
|
||||||
out = this.getJson((response.data as TemplateResponse).template);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out == 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, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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): string {
|
|
||||||
const message = response.data as MessageResponse;
|
|
||||||
if (process.env.BW_RAW === "true") {
|
|
||||||
return message.raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
let out = "";
|
|
||||||
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 exitIfAuthed() {
|
|
||||||
const authed = await firstValueFrom(
|
|
||||||
this.serviceContainer.authService.activeAccountStatus$.pipe(
|
|
||||||
map((status) => status > AuthenticationStatus.LoggedOut),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (authed) {
|
|
||||||
const email = await firstValueFrom(
|
|
||||||
this.serviceContainer.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
|
||||||
);
|
|
||||||
this.processResponse(Response.error("You are already logged in as " + email + "."), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async exitIfNotAuthed() {
|
|
||||||
const authed = await this.serviceContainer.stateService.getIsAuthenticated();
|
|
||||||
if (!authed) {
|
|
||||||
this.processResponse(Response.error("You are not logged in."), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async exitIfLocked() {
|
|
||||||
await this.exitIfNotAuthed();
|
|
||||||
if (await this.serviceContainer.cryptoService.hasUserKey()) {
|
|
||||||
return;
|
|
||||||
} else if (process.env.BW_NOINTERACTION !== "true") {
|
|
||||||
// must unlock
|
|
||||||
if (await this.serviceContainer.keyConnectorService.getUsesKeyConnector()) {
|
|
||||||
const response = Response.error(
|
|
||||||
"Your vault is locked. You must unlock your vault using your session key.\n" +
|
|
||||||
"If you do not have your session key, you can get a new one by logging out and logging in again.",
|
|
||||||
);
|
|
||||||
this.processResponse(response, true);
|
|
||||||
} else {
|
|
||||||
const command = new UnlockCommand(
|
|
||||||
this.serviceContainer.accountService,
|
|
||||||
this.serviceContainer.masterPasswordService,
|
|
||||||
this.serviceContainer.cryptoService,
|
|
||||||
this.serviceContainer.stateService,
|
|
||||||
this.serviceContainer.cryptoFunctionService,
|
|
||||||
this.serviceContainer.apiService,
|
|
||||||
this.serviceContainer.logService,
|
|
||||||
this.serviceContainer.keyConnectorService,
|
|
||||||
this.serviceContainer.environmentService,
|
|
||||||
this.serviceContainer.syncService,
|
|
||||||
this.serviceContainer.organizationApiService,
|
|
||||||
this.serviceContainer.logout,
|
|
||||||
this.serviceContainer.kdfConfigService,
|
|
||||||
);
|
|
||||||
const response = await command.run(null, null);
|
|
||||||
if (!response.success) {
|
|
||||||
this.processResponse(response, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.processResponse(Response.error("Vault is locked."), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,8 @@ export async function registerOssPrograms(serviceContainer: ServiceContainer) {
|
|||||||
await program.register();
|
await program.register();
|
||||||
|
|
||||||
const vaultProgram = new VaultProgram(serviceContainer);
|
const vaultProgram = new VaultProgram(serviceContainer);
|
||||||
await vaultProgram.register();
|
vaultProgram.register();
|
||||||
|
|
||||||
const sendProgram = new SendProgram(serviceContainer);
|
const sendProgram = new SendProgram(serviceContainer);
|
||||||
await sendProgram.register();
|
sendProgram.register();
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,9 @@ import { program, Command, OptionValues } from "commander";
|
|||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||||
|
|
||||||
|
import { BaseProgram } from "../../base-program";
|
||||||
import { GetCommand } from "../../commands/get.command";
|
import { GetCommand } from "../../commands/get.command";
|
||||||
import { Response } from "../../models/response";
|
import { Response } from "../../models/response";
|
||||||
import { Program } from "../../program";
|
|
||||||
import { CliUtils } from "../../utils";
|
import { CliUtils } from "../../utils";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -27,8 +27,8 @@ import { SendResponse } from "./models/send.response";
|
|||||||
|
|
||||||
const writeLn = CliUtils.writeLn;
|
const writeLn = CliUtils.writeLn;
|
||||||
|
|
||||||
export class SendProgram extends Program {
|
export class SendProgram extends BaseProgram {
|
||||||
async register() {
|
register() {
|
||||||
program.addCommand(this.sendCommand());
|
program.addCommand(this.sendCommand());
|
||||||
// receive is accessible both at `bw receive` and `bw send receive`
|
// receive is accessible both at `bw receive` and `bw send receive`
|
||||||
program.addCommand(this.receiveCommand());
|
program.addCommand(this.receiveCommand());
|
||||||
|
@ -2,12 +2,12 @@ import { program, Command } from "commander";
|
|||||||
|
|
||||||
import { ConfirmCommand } from "./admin-console/commands/confirm.command";
|
import { ConfirmCommand } from "./admin-console/commands/confirm.command";
|
||||||
import { ShareCommand } from "./admin-console/commands/share.command";
|
import { ShareCommand } from "./admin-console/commands/share.command";
|
||||||
|
import { BaseProgram } from "./base-program";
|
||||||
import { EditCommand } from "./commands/edit.command";
|
import { EditCommand } from "./commands/edit.command";
|
||||||
import { GetCommand } from "./commands/get.command";
|
import { GetCommand } from "./commands/get.command";
|
||||||
import { ListCommand } from "./commands/list.command";
|
import { ListCommand } from "./commands/list.command";
|
||||||
import { RestoreCommand } from "./commands/restore.command";
|
import { RestoreCommand } from "./commands/restore.command";
|
||||||
import { Response } from "./models/response";
|
import { Response } from "./models/response";
|
||||||
import { Program } from "./program";
|
|
||||||
import { ExportCommand } from "./tools/export.command";
|
import { ExportCommand } from "./tools/export.command";
|
||||||
import { ImportCommand } from "./tools/import.command";
|
import { ImportCommand } from "./tools/import.command";
|
||||||
import { CliUtils } from "./utils";
|
import { CliUtils } from "./utils";
|
||||||
@ -16,8 +16,8 @@ import { DeleteCommand } from "./vault/delete.command";
|
|||||||
|
|
||||||
const writeLn = CliUtils.writeLn;
|
const writeLn = CliUtils.writeLn;
|
||||||
|
|
||||||
export class VaultProgram extends Program {
|
export class VaultProgram extends BaseProgram {
|
||||||
async register() {
|
register() {
|
||||||
program
|
program
|
||||||
.addCommand(this.listCommand())
|
.addCommand(this.listCommand())
|
||||||
.addCommand(this.getCommand())
|
.addCommand(this.getCommand())
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
import { Response } from "@bitwarden/cli/models/response";
|
||||||
|
|
||||||
|
export class ApproveAllCommand {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
async run(organizationId: string): Promise<Response> {
|
||||||
|
throw new Error("Not implemented");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
import { Response } from "@bitwarden/cli/models/response";
|
||||||
|
|
||||||
|
export class ApproveCommand {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
async run(id: string): Promise<Response> {
|
||||||
|
throw new Error("Not implemented");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
import { Response } from "@bitwarden/cli/models/response";
|
||||||
|
|
||||||
|
export class DenyAllCommand {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
async run(organizationId: string): Promise<Response> {
|
||||||
|
throw new Error("Not implemented");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
import { Response } from "@bitwarden/cli/models/response";
|
||||||
|
|
||||||
|
export class DenyCommand {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
async run(id: string): Promise<Response> {
|
||||||
|
throw new Error("Not implemented");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
import { program, Command } from "commander";
|
||||||
|
|
||||||
|
import { BaseProgram } from "@bitwarden/cli/base-program";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
|
||||||
|
import { ApproveAllCommand } from "./approve-all.command";
|
||||||
|
import { ApproveCommand } from "./approve.command";
|
||||||
|
import { DenyAllCommand } from "./deny-all.command";
|
||||||
|
import { DenyCommand } from "./deny.command";
|
||||||
|
import { ListCommand } from "./list.command";
|
||||||
|
|
||||||
|
export class DeviceApprovalProgram extends BaseProgram {
|
||||||
|
register() {
|
||||||
|
program.addCommand(this.deviceApprovalCommand());
|
||||||
|
}
|
||||||
|
|
||||||
|
private deviceApprovalCommand() {
|
||||||
|
return new Command("device-approval")
|
||||||
|
.description("Manage device approvals")
|
||||||
|
.addCommand(this.listCommand())
|
||||||
|
.addCommand(this.approveCommand())
|
||||||
|
.addCommand(this.approveAllCommand())
|
||||||
|
.addCommand(this.denyCommand())
|
||||||
|
.addCommand(this.denyAllCommand());
|
||||||
|
}
|
||||||
|
|
||||||
|
private listCommand(): Command {
|
||||||
|
return new Command("list")
|
||||||
|
.description("List all pending requests for an organization")
|
||||||
|
.argument("<organizationId>")
|
||||||
|
.action(async (organizationId: string) => {
|
||||||
|
await this.exitIfFeatureFlagDisabled(FeatureFlag.BulkDeviceApproval);
|
||||||
|
await this.exitIfNotAuthed();
|
||||||
|
|
||||||
|
const cmd = new ListCommand();
|
||||||
|
const response = await cmd.run(organizationId);
|
||||||
|
this.processResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private approveCommand(): Command {
|
||||||
|
return new Command("approve")
|
||||||
|
.argument("<id>")
|
||||||
|
.description("Approve a pending request")
|
||||||
|
.action(async (id: string) => {
|
||||||
|
await this.exitIfFeatureFlagDisabled(FeatureFlag.BulkDeviceApproval);
|
||||||
|
await this.exitIfLocked();
|
||||||
|
|
||||||
|
const cmd = new ApproveCommand();
|
||||||
|
const response = await cmd.run(id);
|
||||||
|
this.processResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private approveAllCommand(): Command {
|
||||||
|
return new Command("approveAll")
|
||||||
|
.description("Approve all pending requests for an organization")
|
||||||
|
.argument("<organizationId>")
|
||||||
|
.action(async (organizationId: string) => {
|
||||||
|
await this.exitIfFeatureFlagDisabled(FeatureFlag.BulkDeviceApproval);
|
||||||
|
await this.exitIfLocked();
|
||||||
|
|
||||||
|
const cmd = new ApproveAllCommand();
|
||||||
|
const response = await cmd.run(organizationId);
|
||||||
|
this.processResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private denyCommand(): Command {
|
||||||
|
return new Command("deny")
|
||||||
|
.argument("<id>")
|
||||||
|
.description("Deny a pending request")
|
||||||
|
.action(async (id: string) => {
|
||||||
|
await this.exitIfFeatureFlagDisabled(FeatureFlag.BulkDeviceApproval);
|
||||||
|
await this.exitIfLocked();
|
||||||
|
|
||||||
|
const cmd = new DenyCommand();
|
||||||
|
const response = await cmd.run(id);
|
||||||
|
this.processResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private denyAllCommand(): Command {
|
||||||
|
return new Command("denyAll")
|
||||||
|
.description("Deny all pending requests for an organization")
|
||||||
|
.argument("<organizationId>")
|
||||||
|
.action(async (organizationId: string) => {
|
||||||
|
await this.exitIfFeatureFlagDisabled(FeatureFlag.BulkDeviceApproval);
|
||||||
|
await this.exitIfLocked();
|
||||||
|
|
||||||
|
const cmd = new DenyAllCommand();
|
||||||
|
const response = await cmd.run(organizationId);
|
||||||
|
this.processResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export { DeviceApprovalProgram } from "./device-approval.program";
|
@ -0,0 +1,9 @@
|
|||||||
|
import { Response } from "@bitwarden/cli/models/response";
|
||||||
|
|
||||||
|
export class ListCommand {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
async run(organizationId: string): Promise<Response> {
|
||||||
|
throw new Error("Not implemented");
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
import { DeviceApprovalProgram } from "./admin-console/device-approval";
|
||||||
import { ServiceContainer } from "./service-container";
|
import { ServiceContainer } from "./service-container";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -7,4 +8,6 @@ import { ServiceContainer } from "./service-container";
|
|||||||
* myProgram.register();
|
* myProgram.register();
|
||||||
* @param serviceContainer A class that instantiates services and makes them available for dependency injection
|
* @param serviceContainer A class that instantiates services and makes them available for dependency injection
|
||||||
*/
|
*/
|
||||||
export async function registerBitPrograms(serviceContainer: ServiceContainer) {}
|
export async function registerBitPrograms(serviceContainer: ServiceContainer) {
|
||||||
|
new DeviceApprovalProgram(serviceContainer).register();
|
||||||
|
}
|
||||||
|
@ -16,6 +16,7 @@ export enum FeatureFlag {
|
|||||||
ExtensionRefresh = "extension-refresh",
|
ExtensionRefresh = "extension-refresh",
|
||||||
RestrictProviderAccess = "restrict-provider-access",
|
RestrictProviderAccess = "restrict-provider-access",
|
||||||
UseTreeWalkerApiForPageDetailsCollection = "use-tree-walker-api-for-page-details-collection",
|
UseTreeWalkerApiForPageDetailsCollection = "use-tree-walker-api-for-page-details-collection",
|
||||||
|
BulkDeviceApproval = "bulk-device-approval",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AllowedFeatureFlagTypes = boolean | number | string;
|
export type AllowedFeatureFlagTypes = boolean | number | string;
|
||||||
@ -42,6 +43,7 @@ export const DefaultFeatureFlagValue = {
|
|||||||
[FeatureFlag.ExtensionRefresh]: FALSE,
|
[FeatureFlag.ExtensionRefresh]: FALSE,
|
||||||
[FeatureFlag.RestrictProviderAccess]: FALSE,
|
[FeatureFlag.RestrictProviderAccess]: FALSE,
|
||||||
[FeatureFlag.UseTreeWalkerApiForPageDetailsCollection]: FALSE,
|
[FeatureFlag.UseTreeWalkerApiForPageDetailsCollection]: FALSE,
|
||||||
|
[FeatureFlag.BulkDeviceApproval]: FALSE,
|
||||||
} satisfies Record<FeatureFlag, AllowedFeatureFlagTypes>;
|
} satisfies Record<FeatureFlag, AllowedFeatureFlagTypes>;
|
||||||
|
|
||||||
export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue;
|
export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue;
|
||||||
|
Loading…
Reference in New Issue
Block a user