diff --git a/package.json b/package.json index fcd0b38..34b0847 100644 --- a/package.json +++ b/package.json @@ -105,6 +105,12 @@ "title": "Retrieve access token", "category": "debricked", "icon": "$(account)" + }, + { + "command": "debricked.debricked.reset", + "title": "Reset Debricked", + "category": "debricked", + "icon": "$(trash)" } ], "menus": { diff --git a/src/commands/debrickedCommand.ts b/src/commands/debrickedCommand.ts index 4978419..03a58d2 100644 --- a/src/commands/debrickedCommand.ts +++ b/src/commands/debrickedCommand.ts @@ -24,6 +24,7 @@ export class DebrickedCommand { this.registerCommand(context, baseSubCommands[3].command, Logger.openLogFile); this.registerCommand(context, baseSubCommands[4].command, baseCommandService.login); this.registerCommand(context, baseSubCommands[5].command, SentryHelper.reConfigureSentry); + this.registerCommand(context, baseSubCommands[6].command, baseCommandService.reset); } //Register auth sub-commands diff --git a/src/constants/debricked_cli.ts b/src/constants/debricked_cli.ts index e5d9260..a9397fa 100644 --- a/src/constants/debricked_cli.ts +++ b/src/constants/debricked_cli.ts @@ -61,6 +61,12 @@ export class DebrickedCommands { cli_command: "sentry", description: "Debricked sentry logs", }, + { + label: "Reset", + command: "debricked.debricked.reset", + cli_command: "reset", + description: "Reset Debricked", + }, ], flags: [ { diff --git a/src/constants/enums.ts b/src/constants/enums.ts index c2910b3..13a82f4 100644 --- a/src/constants/enums.ts +++ b/src/constants/enums.ts @@ -21,7 +21,7 @@ export enum Environment { TEST = "test", } -export enum TokenType { +export enum Secrets { ACCESS = "access", BEARER = "bearer", } diff --git a/src/constants/index.ts b/src/constants/index.ts index f42e5e2..44a2ddc 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,6 +1,6 @@ import { DebrickedCommands } from "./debricked_cli"; import { Messages } from "./messages"; -import { MessageStatus, Environment, TokenType, SupportedFilesToScan } from "./enums"; +import { MessageStatus, Environment, Secrets, SupportedFilesToScan } from "./enums"; import { Organization } from "./organization"; import { SecondService } from "./secondService"; import { Regex } from "./regex"; @@ -17,7 +17,7 @@ export { PolicyRules, PolicyTriggerEvents, Environment, - TokenType, + Secrets, Icons, SupportedFilesToScan, }; diff --git a/src/constants/messages.ts b/src/constants/messages.ts index 48c6686..732b6e0 100644 --- a/src/constants/messages.ts +++ b/src/constants/messages.ts @@ -30,4 +30,5 @@ export class Messages { static readonly INSTALLATION_ERROR = "Installation script execution failed."; // Miscellaneous Messages + static readonly RESET_SUCCESS = "Successfully reset debricked."; } diff --git a/src/extension.ts b/src/extension.ts index c09e83f..3c8eac0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -21,8 +21,7 @@ export async function activate(context: vscode.ExtensionContext) { progress.report({ increment: progressCount }); const globalState = globalStore.getGlobalStateInstance(); - // For dev - Clears the globalData - uncomment to clear the globalData - // await globalState?.clearAllGlobalData(); + progress.report({ message: "Activating VS Code Extension", increment: (progressCount += 20), diff --git a/src/helpers/apiClient.ts b/src/helpers/apiClient.ts index 2364b85..89f9013 100644 --- a/src/helpers/apiClient.ts +++ b/src/helpers/apiClient.ts @@ -2,7 +2,7 @@ import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosR import { AuthHelper } from "./authHelper"; import { ErrorHandler } from "./errorHandler"; import { Logger } from "./loggerHelper"; -import { TokenType } from "../constants"; +import { Secrets } from "../constants"; import * as Sentry from "@sentry/node"; export class ApiClient { @@ -17,7 +17,7 @@ export class ApiClient { this.axiosInstance.interceptors.request.use( async (config: AxiosRequestConfig): Promise => { - const token = await authHelper.getToken(true, TokenType.BEARER); + const token = await authHelper.getToken(true, Secrets.BEARER); if (token) { config.headers = { ...config.headers, diff --git a/src/helpers/authHelper.ts b/src/helpers/authHelper.ts index a73b4d8..7d473d4 100644 --- a/src/helpers/authHelper.ts +++ b/src/helpers/authHelper.ts @@ -1,4 +1,4 @@ -import { Messages, TokenType } from "../constants/index"; +import { Messages, Secrets } from "../constants/index"; import { ShowInputBoxHelper } from "./showInputBoxHelper"; import { StatusBarMessageHelper } from "./statusBarMessageHelper"; import { Logger } from "./loggerHelper"; @@ -17,7 +17,7 @@ export class AuthHelper { * @param void * @returns Promise */ - async getToken(useDefaultToken = true, tokenKey: TokenType): Promise { + async getToken(useDefaultToken = true, tokenKey: Secrets): Promise { try { let token: string | undefined; const defaultToken: any = await this.globalStore.getGlobalStateInstance()?.getSecretData(tokenKey); @@ -31,12 +31,12 @@ export class AuthHelper { this.logger.logInfo("InputBox Opened for tokens"); token = await this.showInputBoxHelper.promptForInput({ - prompt: tokenKey === TokenType.ACCESS ? Messages.ENTER_ACCESS_TOKEN : Messages.ENTER_BEARER_TOKEN, + prompt: tokenKey === Secrets.ACCESS ? Messages.ENTER_ACCESS_TOKEN : Messages.ENTER_BEARER_TOKEN, ignoreFocusOut: true, password: true, - title: tokenKey === TokenType.ACCESS ? Messages.ACCESS_TOKEN : Messages.BEARER_TOKEN, + title: tokenKey === Secrets.ACCESS ? Messages.ACCESS_TOKEN : Messages.BEARER_TOKEN, placeHolder: - tokenKey === TokenType.ACCESS ? Messages.ENTER_ACCESS_TOKEN : Messages.ENTER_BEARER_TOKEN, + tokenKey === Secrets.ACCESS ? Messages.ENTER_ACCESS_TOKEN : Messages.ENTER_BEARER_TOKEN, }); this.setToken(tokenKey, token); @@ -49,13 +49,13 @@ export class AuthHelper { } } - async setToken(tokenKey: TokenType, token: string | undefined): Promise { + async setToken(tokenKey: Secrets, token: string | undefined): Promise { if (token) { await this.globalStore.getGlobalStateInstance()?.setSecretData(tokenKey, token); - const message = tokenKey === TokenType.ACCESS ? Messages.ACCESS_TOKEN_SAVED : Messages.BEARER_TOKEN_SAVED; + const message = tokenKey === Secrets.ACCESS ? Messages.ACCESS_TOKEN_SAVED : Messages.BEARER_TOKEN_SAVED; this.statusBarMessageHelper.showInformationMessage(message); } else { - const message = tokenKey === TokenType.ACCESS ? Messages.ACCESS_TOKEN_RQD : Messages.BEARER_TOKEN_RQD; + const message = tokenKey === Secrets.ACCESS ? Messages.ACCESS_TOKEN_RQD : Messages.BEARER_TOKEN_RQD; throw new Error(message); } } diff --git a/src/helpers/commandHelper.ts b/src/helpers/commandHelper.ts index 3118f38..8e4621a 100644 --- a/src/helpers/commandHelper.ts +++ b/src/helpers/commandHelper.ts @@ -1,4 +1,4 @@ -import { DebrickedCommands, Messages, MessageStatus, TokenType } from "../constants/index"; +import { DebrickedCommands, Messages, MessageStatus, Secrets } from "../constants/index"; import { exec } from "child_process"; import * as vscode from "vscode"; import { promisify } from "util"; @@ -31,7 +31,7 @@ export class Command { if (accessTokenRequired) { const globalFlags = DebrickedCommands.getCommandSpecificFlags("Debricked", true) || []; - const accessToken = await this.authHelper.getToken(true, TokenType.ACCESS); + const accessToken = await this.authHelper.getToken(true, Secrets.ACCESS); if (accessToken) { this.logger.logMessageByStatus( @@ -56,7 +56,8 @@ export class Command { } span.end(new Date()); return stdout.trim(); - } catch (error) { + } catch (error: any) { + this.logger.logMessageByStatus(MessageStatus.ERROR, `command error: ${error.message}`); this.logger.logError("Error in executeAsyncCommand"); throw error; } diff --git a/src/helpers/globalState.ts b/src/helpers/globalState.ts index e591be2..8272ace 100644 --- a/src/helpers/globalState.ts +++ b/src/helpers/globalState.ts @@ -1,3 +1,4 @@ +import { Secrets } from "../constants"; import * as vscode from "vscode"; export class GlobalState { @@ -59,4 +60,11 @@ export class GlobalState { public deleteSecretData(key: string): Thenable { return this.context.secrets.delete(key); } + + public async resetDebrickedData(): Promise { + for (const secret in Secrets) { + this.context.secrets.delete(Secrets[secret as keyof typeof Secrets]); + } + await this.clearAllGlobalData(); + } } diff --git a/src/helpers/statusBarMessageHelper.ts b/src/helpers/statusBarMessageHelper.ts index d728e47..8051e6c 100644 --- a/src/helpers/statusBarMessageHelper.ts +++ b/src/helpers/statusBarMessageHelper.ts @@ -23,6 +23,10 @@ export class StatusBarMessageHelper { return await vscode.window.showInformationMessage(`${Organization.nameCaps}: ` + message, ...items); } + public async showWarningMessageWithItems(message: string, items: string[]): Promise { + return await vscode.window.showWarningMessage(`${Organization.nameCaps}: ` + message, ...items); + } + public showWarningMessage(message: string): void { vscode.window.showWarningMessage(`${Organization.nameCaps}: ` + message); } diff --git a/src/helpers/terminalHelper.ts b/src/helpers/terminalHelper.ts index 84f1ee0..47698d1 100644 --- a/src/helpers/terminalHelper.ts +++ b/src/helpers/terminalHelper.ts @@ -1,4 +1,4 @@ -import { DebrickedCommands, Messages, MessageStatus, Organization, TokenType } from "../constants/index"; +import { DebrickedCommands, Messages, MessageStatus, Organization, Secrets } from "../constants/index"; import { AuthHelper } from "./authHelper"; import { Logger } from "./loggerHelper"; @@ -19,7 +19,7 @@ export class Terminal { let command: string = Organization.debrickedCli; if (accessTokenRequired) { const flags = DebrickedCommands.getCommandSpecificFlags("Debricked") || []; - const accessToken = await this.authHelper.getToken(useDefaultAccessToken, TokenType.ACCESS); + const accessToken = await this.authHelper.getToken(useDefaultAccessToken, Secrets.ACCESS); if (accessToken) { this.logger.logMessageByStatus( diff --git a/src/services/baseCommandService.ts b/src/services/baseCommandService.ts index ec98efe..7523694 100644 --- a/src/services/baseCommandService.ts +++ b/src/services/baseCommandService.ts @@ -1,5 +1,5 @@ import { DebrickedCommandNode } from "../types"; -import { DebrickedCommands, Messages, MessageStatus, Organization, TokenType } from "../constants/index"; +import { DebrickedCommands, Messages, MessageStatus, Organization, Secrets } from "../constants/index"; import { statusBarMessageHelper, terminal, @@ -108,8 +108,8 @@ export class BaseCommandService { public async installCommand() { try { - Logger.logMessageByStatus(MessageStatus.INFO, "Register InstallCommand"); SentryHelper.setTransactionName("Install CLI"); + Logger.logMessageByStatus(MessageStatus.INFO, "Register InstallCommand"); const currentVersion = await this.getCurrentExtensionVersion(); Logger.logMessageByStatus( @@ -140,8 +140,8 @@ export class BaseCommandService { public async login(updateCredentials = true) { try { - Logger.logInfo("Register login"); SentryHelper.setTransactionName("Login"); + Logger.logInfo("Register login"); const debrickedData: any = await globalStore .getGlobalStateInstance() @@ -177,7 +177,7 @@ export class BaseCommandService { throw new Error(bearerToken.message); } else { const newBearerToken = `Bearer ${bearerToken.token}`; - await authHelper.setToken(TokenType.BEARER, newBearerToken); + await authHelper.setToken(Secrets.BEARER, newBearerToken); Logger.logInfo(`Login successful. Authentication Bearer token generated for secure access.`); } @@ -193,8 +193,8 @@ export class BaseCommandService { public async updateCommand() { try { - Logger.logMessageByStatus(MessageStatus.INFO, "Register UpdateCommand"); SentryHelper.setTransactionName("Update Token"); + Logger.logMessageByStatus(MessageStatus.INFO, "Register UpdateCommand"); let subCommand: DebrickedCommandNode[] | undefined; if (DebrickedCommands.BASE_COMMAND.sub_commands) { subCommand = DebrickedCommands.BASE_COMMAND.sub_commands[1].sub_commands; @@ -206,10 +206,10 @@ export class BaseCommandService { } switch (selectedSubCommand?.cli_command) { case "accessToken": - authHelper.getToken(false, TokenType.ACCESS); + authHelper.getToken(false, Secrets.ACCESS); break; case "bearerToken": - authHelper.getToken(false, TokenType.BEARER); + authHelper.getToken(false, Secrets.BEARER); break; } } catch (error: any) { @@ -221,6 +221,24 @@ export class BaseCommandService { } } + public async reset() { + try { + SentryHelper.setTransactionName("Reset Debricked"); + Logger.logMessageByStatus(MessageStatus.INFO, "Register ResetCommand"); + + const response = await statusBarMessageHelper.showWarningMessageWithItems( + "Do you want to reset Debricked?", + ["Yes", "No"], + ); + if (response === "Yes") { + await globalStore.getGlobalStateInstance()?.resetDebrickedData(); + statusBarMessageHelper.showInformationMessage(Messages.RESET_SUCCESS); + SentryHelper.captureMessage("Reset Debricked"); + } + } catch (error: any) { + errorHandler.handleError(error); + } + } public async getCurrentExtensionVersion(): Promise { const extension = vscode.extensions.getExtension(`${Organization.name}.${Organization.packageJson.name}`); return extension ? extension.packageJSON.version : Organization.packageJson.version; diff --git a/src/services/scanService.ts b/src/services/scanService.ts index 9992f58..f372956 100644 --- a/src/services/scanService.ts +++ b/src/services/scanService.ts @@ -11,7 +11,7 @@ import { debrickedServiceHelper, showQuickPickHelper, } from "../helpers"; -import { DebrickedCommands, Icons, MessageStatus, Organization, SecondService, TokenType } from "../constants/index"; +import { DebrickedCommands, Icons, MessageStatus, Organization, SecondService, Secrets } from "../constants/index"; import { DebrickedCommandNode, Flag, Repository, RepositoryInfo } from "../types"; import * as vscode from "vscode"; import * as fs from "fs"; @@ -186,7 +186,7 @@ export class ScanService { break; case "-t": { - const accessToken = await authHelper.getToken(true, TokenType.ACCESS); + const accessToken = await authHelper.getToken(true, Secrets.ACCESS); if (accessToken) { cmdParams.push(accessToken); Logger.logMessageByStatus(MessageStatus.INFO, "Access token added"); diff --git a/src/test/helpers/authHelper.test.ts b/src/test/helpers/authHelper.test.ts index 37e5538..2c45f52 100644 --- a/src/test/helpers/authHelper.test.ts +++ b/src/test/helpers/authHelper.test.ts @@ -1,7 +1,7 @@ import { AuthHelper } from "../../helpers/authHelper"; import { Logger, showInputBoxHelper, statusBarMessageHelper } from "../../helpers"; import { sinon, expect } from "../setup"; -import { Messages, TokenType } from "../../constants"; +import { Messages, Secrets } from "../../constants"; describe("Authorization Helper", () => { let authHelper: AuthHelper; @@ -37,12 +37,12 @@ describe("Authorization Helper", () => { sandbox.restore(); }); - const tokenTypes = [ - { type: TokenType.ACCESS, message: Messages.ACCESS_TOKEN_SAVED, promptMessage: Messages.ENTER_ACCESS_TOKEN }, - { type: TokenType.BEARER, message: Messages.BEARER_TOKEN_SAVED, promptMessage: Messages.ENTER_BEARER_TOKEN }, + const secrets = [ + { type: Secrets.ACCESS, message: Messages.ACCESS_TOKEN_SAVED, promptMessage: Messages.ENTER_ACCESS_TOKEN }, + { type: Secrets.BEARER, message: Messages.BEARER_TOKEN_SAVED, promptMessage: Messages.ENTER_BEARER_TOKEN }, ]; - tokenTypes.forEach(({ type, message, promptMessage }) => { + secrets.forEach(({ type, message, promptMessage }) => { describe(`${type} token`, () => { it(`should use default ${type} token`, async () => { const token = await authHelper.getToken(true, type); @@ -62,7 +62,7 @@ describe("Authorization Helper", () => { expect(promptForInputStub.calledOnce).to.be.true; expect(promptForInputStub.firstCall.args[0]).to.include({ prompt: promptMessage, - title: type === TokenType.ACCESS ? Messages.ACCESS_TOKEN : Messages.BEARER_TOKEN, + title: type === Secrets.ACCESS ? Messages.ACCESS_TOKEN : Messages.BEARER_TOKEN, placeHolder: promptMessage, }); expect(token).to.equal(NEW_TOKEN); @@ -86,9 +86,9 @@ describe("Authorization Helper", () => { describe("setToken", () => { it("should set the token and show a success message", async () => { - await authHelper.setToken(TokenType.ACCESS, NEW_TOKEN); + await authHelper.setToken(Secrets.ACCESS, NEW_TOKEN); - expect(globalStateInstance.setSecretData.calledOnceWith(TokenType.ACCESS, NEW_TOKEN)).to.be.true; + expect(globalStateInstance.setSecretData.calledOnceWith(Secrets.ACCESS, NEW_TOKEN)).to.be.true; expect(statusBarMessageHelperStub.calledOnceWith(Messages.ACCESS_TOKEN_SAVED)).to.be.true; }); });