git extension api: registerCredentialsProvider

This commit is contained in:
João Moreno
2020-04-24 18:04:36 +02:00
parent 77d7351226
commit cab6f958a2
8 changed files with 94 additions and 47 deletions

View File

@@ -3,36 +3,60 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { window, InputBoxOptions } from 'vscode';
import { IDisposable } from './util';
import { window, InputBoxOptions, Uri, OutputChannel, Disposable } from 'vscode';
import { IDisposable, EmptyDisposable, toDisposable } from './util';
import * as path from 'path';
import { IIPCHandler, IIPCServer } from './ipc/ipcServer';
export interface AskpassEnvironment {
GIT_ASKPASS: string;
ELECTRON_RUN_AS_NODE?: string;
VSCODE_GIT_ASKPASS_NODE?: string;
VSCODE_GIT_ASKPASS_MAIN?: string;
VSCODE_GIT_ASKPASS_HANDLE?: string;
}
import { IIPCHandler, IIPCServer, createIPCServer } from './ipc/ipcServer';
import { CredentialsProvider, Credentials } from './api/git';
export class Askpass implements IIPCHandler {
private disposable: IDisposable;
private disposable: IDisposable = EmptyDisposable;
private cache = new Map<string, Credentials>();
private credentialsProviders = new Set<CredentialsProvider>();
static getDisabledEnv(): AskpassEnvironment {
return {
GIT_ASKPASS: path.join(__dirname, 'askpass-empty.sh')
};
static async create(outputChannel: OutputChannel): Promise<Askpass> {
try {
return new Askpass(await createIPCServer());
} catch (err) {
outputChannel.appendLine(`[error] Failed to create git askpass IPC: ${err}`);
return new Askpass();
}
}
constructor(ipc: IIPCServer) {
this.disposable = ipc.registerHandler('askpass', this);
private constructor(private ipc?: IIPCServer) {
if (ipc) {
this.disposable = ipc.registerHandler('askpass', this);
}
}
async handle({ request, host }: { request: string, host: string }): Promise<string> {
const uri = Uri.parse(host);
const authority = uri.authority.replace(/^.*@/, '');
const password = /password/i.test(request);
const cached = this.cache.get(authority);
if (cached && password) {
this.cache.delete(authority);
return cached.password;
}
if (!password) {
for (const credentialsProvider of this.credentialsProviders) {
try {
const credentials = await credentialsProvider.getCredentials(uri);
if (credentials) {
this.cache.set(authority, credentials);
setTimeout(() => this.cache.delete(authority), 60_000);
return credentials.username;
}
} catch { }
}
}
const options: InputBoxOptions = {
password: /password/i.test(request),
password,
placeHolder: request,
prompt: `Git: ${host}`,
ignoreFocusOut: true
@@ -41,8 +65,15 @@ export class Askpass implements IIPCHandler {
return await window.showInputBox(options) || '';
}
getEnv(): AskpassEnvironment {
getEnv(): { [key: string]: string; } {
if (!this.ipc) {
return {
GIT_ASKPASS: path.join(__dirname, 'askpass-empty.sh')
};
}
return {
...this.ipc.getEnv(),
ELECTRON_RUN_AS_NODE: '1',
GIT_ASKPASS: path.join(__dirname, 'askpass.sh'),
VSCODE_GIT_ASKPASS_NODE: process.execPath,
@@ -50,6 +81,11 @@ export class Askpass implements IIPCHandler {
};
}
registerCredentialsProvider(provider: CredentialsProvider): Disposable {
this.credentialsProviders.add(provider);
return toDisposable(() => this.credentialsProviders.delete(provider));
}
dispose(): void {
this.disposable.dispose();
}