Skip to content

Commit

Permalink
Merge pull request #15 from GitGuardian/salomevoltz/add-api-ket-to-se…
Browse files Browse the repository at this point in the history
…ttings

Fix authentication flow to include API key
  • Loading branch information
salome-voltz authored Sep 24, 2024
2 parents d741471 + c310eaf commit d2e34b5
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 67 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
"type": "string",
"default": "https://api.gitguardian.com",
"markdownDescription": "You can override the value here for On Premise installations"
},
"gitguardian.apiKey": {
"type": "string",
"default": "",
"markdownDescription": "Your API Key"
}
}
},
Expand Down
13 changes: 10 additions & 3 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ggshieldApiKey,
ggshieldAuthStatus,
ggshieldScanFile,
ignoreLastFound,
Expand All @@ -7,8 +8,9 @@ import {
showAPIQuota,
} from "./lib/ggshield-api";
import {
createDefaultConfiguration,
getConfiguration,
GGShieldConfiguration,
setApiKey,
} from "./lib/ggshield-configuration";
import { parseGGShieldResults } from "./lib/ggshield-results-parser";
import {
Expand Down Expand Up @@ -121,7 +123,7 @@ function registerQuotaViewCommands(view: GitGuardianQuotaWebviewProvider) {
export function activate(context: ExtensionContext) {
// Check if ggshield if available
const outputChannel = window.createOutputChannel("GitGuardian");
let configuration = createDefaultConfiguration(context);
let configuration = getConfiguration(context);
let authStatus: boolean = false;
const ggshieldResolver = new GGShieldResolver(
outputChannel,
Expand Down Expand Up @@ -165,7 +167,10 @@ export function activate(context: ExtensionContext) {
updateStatusBarItem(StatusBarStatus.unauthenticated, statusBar);
} else {
commands.executeCommand('setContext', 'isAuthenticated', true);
}
updateStatusBarItem(StatusBarStatus.ready, statusBar);
const ggshieldApi = ggshieldApiKey(configuration);
setApiKey(configuration, ggshieldApi);
}
})
.then(async () => {
// Check if git is installed
Expand Down Expand Up @@ -233,6 +238,8 @@ export function activate(context: ExtensionContext) {
authStatus = true;
updateStatusBarItem(StatusBarStatus.ready, statusBar);
commands.executeCommand('setContext', 'isAuthenticated', true);
const ggshieldApi = ggshieldApiKey(configuration);
setApiKey(configuration, ggshieldApi);
ggshieldViewProvider.refresh();
ggshieldQuotaViewProvider.refresh();
} else {
Expand Down
55 changes: 47 additions & 8 deletions src/lib/ggshield-api.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/naming-convention */
import {
SpawnOptionsWithoutStdio,
SpawnSyncOptionsWithStringEncoding,
Expand All @@ -21,16 +22,26 @@ export function runGGShieldCommand(
configuration: GGShieldConfiguration,
args: string[]
): SpawnSyncReturns<string> {
const { ggshieldPath, apiUrl } = configuration;
const { ggshieldPath, apiUrl, apiKey } = configuration;
let env: {
GITGUARDIAN_API_URL: string;
GG_USER_AGENT: string;
GITGUARDIAN_API_KEY?: string; // Note the ? to indicate this property is optional
} = {
GITGUARDIAN_API_URL: apiUrl,
GG_USER_AGENT: "gitguardian-vscode",
};

if (apiKey) {
env = {
...env,
// eslint-disable-next-line @typescript-eslint/naming-convention
GITGUARDIAN_API_KEY: apiKey,
};
}

let options: SpawnSyncOptionsWithStringEncoding = {
cwd: os.tmpdir(),
env: {
// eslint-disable-next-line @typescript-eslint/naming-convention
GITGUARDIAN_API_URL: apiUrl,
// eslint-disable-next-line @typescript-eslint/naming-convention
GG_USER_AGENT: "gitguardian-vscode",
},
encoding: "utf-8",
windowsHide: true,
};
Expand Down Expand Up @@ -178,7 +189,7 @@ export async function loginGGShield(
configuration: GGShieldConfiguration,
outputChannel: any
): Promise<boolean> {
const { ggshieldPath, apiUrl } = configuration;
const { ggshieldPath, apiUrl, apiKey } = configuration;

let options: SpawnOptionsWithoutStdio = {
cwd: os.tmpdir(),
Expand Down Expand Up @@ -230,7 +241,35 @@ export function ggshieldAuthStatus(
console.log(proc.stderr);
return false;
} else {
if (proc.stdout.includes("unhealthy")) {
return false;
}
console.log(proc.stdout);
return true;
}
}

export function ggshieldApiKey(
configuration: GGShieldConfiguration,
): string | undefined {
const proc = runGGShieldCommand(configuration, ["config", "list"]);
if (proc.stderr || proc.error) {
console.log(proc.stderr);
return undefined;
} else {
console.log(proc.stdout);
const apiUrl = configuration.apiUrl;
const re = /api/;

const regexInstanceSection = `\\[${apiUrl.replace(re, "dashboard")}\\]([\\s\\S]*?)(?=\\[|$)`;
const instanceSectionMatch = proc.stdout.match(regexInstanceSection);

if (instanceSectionMatch) {
const instanceSection = instanceSectionMatch[0];
const regexToken = /token:\s([a-zA-Z0-9]+)/;
const matchToken = instanceSection.match(regexToken);

return matchToken ? matchToken[1].trim() : undefined;
}
}
}
36 changes: 20 additions & 16 deletions src/lib/ggshield-configuration.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { getBinaryAbsolutePath } from "./ggshield-resolver-utils";
import { ExtensionContext, workspace } from "vscode";
import { ConfigurationTarget, ExtensionContext, workspace } from "vscode";
import * as os from "os";

const apiUrlDefault = "https://api.gitguardian.com/";

export class GGShieldConfiguration {
ggshieldPath: string;
apiUrl: string;
apiKey: string;

constructor(ggshieldPath: string = "", apiUrl: string = "") {
constructor(ggshieldPath: string = "", apiUrl: string = "", apiKey: string = "") {
this.ggshieldPath = ggshieldPath;
this.apiUrl = apiUrl;
this.apiKey = apiKey;
}
}

Expand All @@ -20,26 +22,28 @@ export class GGShieldConfiguration {
* TODO: Check with Mathieu if this behaviour is expected
* @returns {GGShieldConfiguration} from the extension settings
*/
export function getSettingsConfiguration(): GGShieldConfiguration | undefined {
export function getConfiguration(
context: ExtensionContext
): GGShieldConfiguration {
const config = workspace.getConfiguration("gitguardian");

const ggshieldPath: string | undefined = config.get("GGShieldPath");
const apiUrl: string | undefined = config.get("apiUrl");
const apiKey: string | undefined = config.get("apiKey");

if (!ggshieldPath) {
return undefined;
}
return new GGShieldConfiguration(
ggshieldPath,
apiUrl ? apiUrl : apiUrlDefault
ggshieldPath ? ggshieldPath : getBinaryAbsolutePath(os.platform(), os.arch(), context),
apiUrl ? apiUrl : apiUrlDefault,
apiKey ? apiKey : ""
);
}

export function createDefaultConfiguration(
context: ExtensionContext
): GGShieldConfiguration {
return new GGShieldConfiguration(
getBinaryAbsolutePath(os.platform(), os.arch(), context),
"https://api.gitguardian.com/"
);
}
export function setApiKey(configuration: GGShieldConfiguration, apiKey: string | undefined): void {
const config = workspace.getConfiguration("gitguardian");
if (!apiKey) {
throw new Error("Missing API Key");
}

configuration.apiKey = apiKey;
config.update("apiKey", apiKey, ConfigurationTarget.Global);
}
56 changes: 16 additions & 40 deletions src/lib/ggshield-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as vscode from "vscode";
import {
getSettingsConfiguration,
getConfiguration,
GGShieldConfiguration,
} from "./ggshield-configuration";
import { runGGShieldCommand } from "./ggshield-api";
Expand Down Expand Up @@ -32,44 +32,20 @@ export class GGShieldResolver {
* @returns {Promise<void>} A promise that resolves once the `ggshield` path is determined.
*/
async checkGGShieldConfiguration(): Promise<void> {
let settingsConfiguration = getSettingsConfiguration();
if (settingsConfiguration) {
try {
await this.useSettingsConfiguration(settingsConfiguration);
this.channel.appendLine(
`Using ggshield at: ${this.configuration.ggshieldPath}, to change this go to settings.`
);
return;
} catch (error) {
this.channel.appendLine(
`Failed to use ggshield version from settings.
You can remove it from settings, and use the bundled version instead.`
);
window.showErrorMessage(
`Failed to use ggshield version from settings.`
);
throw error;
}
} else {
try {
await this.checkBundledGGShield();
this.channel.appendLine(
`Using bundled ggshield at: ${this.configuration.ggshieldPath}, to change this go to settings.`
);
return;
} catch (error) {
this.channel.appendLine(
`ggshield binary not found: this architecture is not supported ${
(os.arch(), os.platform())
}`
);
window.showErrorMessage(
`ggshield binary not found: this architecture is not supported ${
(os.arch(), os.platform())
}`
);
throw error;
}
try {
await this.testConfiguration(this.configuration);
this.channel.appendLine(
`Using ggshield at: ${this.configuration.ggshieldPath}, to change this go to settings.`
);
return;
} catch (error) {
this.channel.appendLine(
`Failed to use ggshield version ${this.configuration.ggshieldPath}.`
);
window.showErrorMessage(
`Failed to use ggshield.`
);
throw error;
}
}

Expand All @@ -78,7 +54,7 @@ export class GGShieldResolver {
*
* @returns {Promise<void>} A promise that resolves if the configuration is valid.
*/
async useSettingsConfiguration(
async testConfiguration(
configuration: GGShieldConfiguration
): Promise<void> {
let proc = runGGShieldCommand(configuration, ["--version"]);
Expand Down

0 comments on commit d2e34b5

Please sign in to comment.