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/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/common/src/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 { program, Command, OptionValues } from "commander";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
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 { LogoutCommand } from "./auth/commands/logout.command";
|
||||
import { UnlockCommand } from "./auth/commands/unlock.command";
|
||||
import { BaseProgram } from "./base-program";
|
||||
import { CompletionCommand } from "./commands/completion.command";
|
||||
import { ConfigCommand } from "./commands/config.command";
|
||||
import { EncodeCommand } from "./commands/encode.command";
|
||||
@ -15,20 +16,14 @@ import { ServeCommand } from "./commands/serve.command";
|
||||
import { StatusCommand } from "./commands/status.command";
|
||||
import { UpdateCommand } from "./commands/update.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 { GenerateCommand } from "./tools/generate.command";
|
||||
import { CliUtils } from "./utils";
|
||||
import { SyncCommand } from "./vault/sync.command";
|
||||
|
||||
const writeLn = CliUtils.writeLn;
|
||||
|
||||
export class Program {
|
||||
constructor(protected serviceContainer: ServiceContainer) {}
|
||||
|
||||
export class Program extends BaseProgram {
|
||||
async register() {
|
||||
program
|
||||
.option("--pretty", "Format output. JSON is tabbed with two spaces.")
|
||||
@ -517,147 +512,4 @@ export class Program {
|
||||
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();
|
||||
|
||||
const vaultProgram = new VaultProgram(serviceContainer);
|
||||
await vaultProgram.register();
|
||||
vaultProgram.register();
|
||||
|
||||
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 { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||
|
||||
import { BaseProgram } from "../../base-program";
|
||||
import { GetCommand } from "../../commands/get.command";
|
||||
import { Response } from "../../models/response";
|
||||
import { Program } from "../../program";
|
||||
import { CliUtils } from "../../utils";
|
||||
|
||||
import {
|
||||
@ -27,8 +27,8 @@ import { SendResponse } from "./models/send.response";
|
||||
|
||||
const writeLn = CliUtils.writeLn;
|
||||
|
||||
export class SendProgram extends Program {
|
||||
async register() {
|
||||
export class SendProgram extends BaseProgram {
|
||||
register() {
|
||||
program.addCommand(this.sendCommand());
|
||||
// receive is accessible both at `bw receive` and `bw send receive`
|
||||
program.addCommand(this.receiveCommand());
|
||||
|
@ -2,12 +2,12 @@ import { program, Command } from "commander";
|
||||
|
||||
import { ConfirmCommand } from "./admin-console/commands/confirm.command";
|
||||
import { ShareCommand } from "./admin-console/commands/share.command";
|
||||
import { BaseProgram } from "./base-program";
|
||||
import { EditCommand } from "./commands/edit.command";
|
||||
import { GetCommand } from "./commands/get.command";
|
||||
import { ListCommand } from "./commands/list.command";
|
||||
import { RestoreCommand } from "./commands/restore.command";
|
||||
import { Response } from "./models/response";
|
||||
import { Program } from "./program";
|
||||
import { ExportCommand } from "./tools/export.command";
|
||||
import { ImportCommand } from "./tools/import.command";
|
||||
import { CliUtils } from "./utils";
|
||||
@ -16,8 +16,8 @@ import { DeleteCommand } from "./vault/delete.command";
|
||||
|
||||
const writeLn = CliUtils.writeLn;
|
||||
|
||||
export class VaultProgram extends Program {
|
||||
async register() {
|
||||
export class VaultProgram extends BaseProgram {
|
||||
register() {
|
||||
program
|
||||
.addCommand(this.listCommand())
|
||||
.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";
|
||||
|
||||
/**
|
||||
@ -7,4 +8,6 @@ import { ServiceContainer } from "./service-container";
|
||||
* myProgram.register();
|
||||
* @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",
|
||||
RestrictProviderAccess = "restrict-provider-access",
|
||||
UseTreeWalkerApiForPageDetailsCollection = "use-tree-walker-api-for-page-details-collection",
|
||||
BulkDeviceApproval = "bulk-device-approval",
|
||||
}
|
||||
|
||||
export type AllowedFeatureFlagTypes = boolean | number | string;
|
||||
@ -42,6 +43,7 @@ export const DefaultFeatureFlagValue = {
|
||||
[FeatureFlag.ExtensionRefresh]: FALSE,
|
||||
[FeatureFlag.RestrictProviderAccess]: FALSE,
|
||||
[FeatureFlag.UseTreeWalkerApiForPageDetailsCollection]: FALSE,
|
||||
[FeatureFlag.BulkDeviceApproval]: FALSE,
|
||||
} satisfies Record<FeatureFlag, AllowedFeatureFlagTypes>;
|
||||
|
||||
export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue;
|
||||
|
Loading…
Reference in New Issue
Block a user