Updated credentials system (#17888)

* linting

* added flags

* remove testing values

* format

* format doc

* tested in linux

* remove unused interface

* comments

* review comments

* clean imports

* pr comments

* format doc

* changed promise location

* insiders december

* pr comments

* test ado change

* fix test

* comment out code for hygiene

* remove unused imports

* test creds from client only

* remove unused import

* trying enabling keytar

* trying enabling keytar

* disable in correct script

* print statements

* remove print statements

* check mock output

* add linux check

* remove print statements
This commit is contained in:
Aditya Bist
2021-12-17 10:55:42 -08:00
committed by GitHub
parent d8467f2c1a
commit 2a681605fa
10 changed files with 198 additions and 142 deletions

View File

@@ -3,63 +3,13 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SqlOpsDataClient, ClientOptions, SqlOpsFeature } from 'dataprotocol-client';
import { SqlOpsDataClient, ClientOptions } from 'dataprotocol-client';
import { IConfig } from '@microsoft/ads-service-downloader';
import { ServerOptions, RPCMessageType, ClientCapabilities, ServerCapabilities, TransportKind } from 'vscode-languageclient';
import { Disposable } from 'vscode';
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
import * as azdata from 'azdata';
import * as Contracts from './contracts';
import { ServerOptions, TransportKind } from 'vscode-languageclient';
import * as Constants from './constants';
import * as Utils from '../utils';
class CredentialsFeature extends SqlOpsFeature<any> {
private static readonly messagesTypes: RPCMessageType[] = [
Contracts.DeleteCredentialRequest.type,
Contracts.SaveCredentialRequest.type,
Contracts.ReadCredentialRequest.type
];
constructor(client: SqlOpsDataClient) {
super(client, CredentialsFeature.messagesTypes);
}
fillClientCapabilities(capabilities: ClientCapabilities): void {
Utils.ensure(Utils.ensure(capabilities, 'credentials')!, 'credentials')!.dynamicRegistration = true;
}
initialize(capabilities: ServerCapabilities): void {
this.register(this.messages, {
id: UUID.generateUuid(),
registerOptions: undefined
});
}
protected registerProvider(options: any): Disposable {
const client = this._client;
let readCredential = (credentialId: string): Thenable<azdata.Credential> => {
return client.sendRequest(Contracts.ReadCredentialRequest.type, { credentialId, password: undefined });
};
let saveCredential = (credentialId: string, password: string): Thenable<boolean> => {
return client.sendRequest(Contracts.SaveCredentialRequest.type, { credentialId, password });
};
let deleteCredential = (credentialId: string): Thenable<boolean> => {
return client.sendRequest(Contracts.DeleteCredentialRequest.type, { credentialId, password: undefined });
};
return azdata.credentials.registerProvider({
deleteCredential,
readCredential,
saveCredential,
handle: 0
});
}
}
import { SqlCredentialService } from './sqlCredentialService';
import { AppContext } from '../appContext';
/**
* Implements a credential storage for Windows, Mac (darwin), or Linux.
@@ -69,18 +19,24 @@ class CredentialsFeature extends SqlOpsFeature<any> {
export class CredentialStore {
private _client: SqlOpsDataClient;
private _config: IConfig;
private _logPath: string;
constructor(private logPath: string, baseConfig: IConfig) {
constructor(
private context: AppContext,
baseConfig: IConfig
) {
if (baseConfig) {
this._config = JSON.parse(JSON.stringify(baseConfig));
this._config.executableFiles = ['MicrosoftSqlToolsCredentials.exe', 'MicrosoftSqlToolsCredentials'];
}
this.context = context;
this._logPath = this.context.extensionContext.logPath;
}
public async start(): Promise<void> {
let clientOptions: ClientOptions = {
providerId: Constants.providerId,
features: [CredentialsFeature]
features: [SqlCredentialService.asFeature(this.context)]
};
const serverPath = await Utils.getOrDownloadServer(this._config);
const serverOptions = this.generateServerOptions(serverPath);
@@ -95,7 +51,7 @@ export class CredentialStore {
}
private generateServerOptions(executablePath: string): ServerOptions {
let launchArgs = Utils.getCommonLaunchArgsAndCleanupOldLogFiles(this.logPath, 'credentialstore.log', executablePath);
let launchArgs = Utils.getCommonLaunchArgsAndCleanupOldLogFiles(this._logPath, 'credentialstore.log', executablePath);
return { command: executablePath, args: launchArgs, transport: TransportKind.stdio };
}
}

View File

@@ -0,0 +1,91 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AppContext } from '../appContext';
import { SqlOpsDataClient, ISqlOpsFeature, SqlOpsFeature } from 'dataprotocol-client';
import * as Utils from '../utils';
import { ClientCapabilities, RPCMessageType, ServerCapabilities } from 'vscode-languageclient';
import * as Contracts from './contracts';
import { Disposable, SecretStorage } from 'vscode';
import * as azdata from 'azdata';
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
export class SqlCredentialService extends SqlOpsFeature<any> {
private static readonly messagesTypes: RPCMessageType[] = [
Contracts.DeleteCredentialRequest.type,
Contracts.SaveCredentialRequest.type,
Contracts.ReadCredentialRequest.type
];
public static asFeature(context: AppContext): ISqlOpsFeature {
return class extends SqlCredentialService {
private _secretStorage: SecretStorage;
constructor(client: SqlOpsDataClient) {
super(context, client);
this._secretStorage = context.extensionContext.secrets;
}
override fillClientCapabilities(capabilities: ClientCapabilities): void {
Utils.ensure(Utils.ensure(capabilities, 'credentials')!, 'credentials')!.dynamicRegistration = true;
}
override async initialize(capabilities: ServerCapabilities): Promise<void> {
this.register(this.messages, {
id: UUID.generateUuid(),
registerOptions: undefined
});
}
protected override registerProvider(options: any): Disposable {
let readCredential = async (credentialId: string): Promise<azdata.Credential> => {
return this._client.sendRequest(Contracts.ReadCredentialRequest.type, { credentialId, password: undefined });
};
let saveCredential = async (credentialId: string, password: string): Promise<boolean> => {
if (Utils.isLinux) {
/**
* This is only done for linux because this is going to be
* the default credential system for linux in the next release
*/
await this._secretStorage.store(credentialId, password);
}
return this._client.sendRequest(Contracts.SaveCredentialRequest.type, { credentialId, password });
};
let deleteCredential = async (credentialId: string): Promise<boolean> => {
if (Utils.isLinux) {
try {
await this._secretStorage.delete(credentialId);
} catch (e) {
console.log('credential does not exist in native secret store');
}
}
return this._client.sendRequest(Contracts.DeleteCredentialRequest.type, { credentialId, password: undefined });
};
return azdata.credentials.registerProvider({
deleteCredential,
readCredential,
saveCredential,
handle: 0
});
}
};
}
fillClientCapabilities(capabilities: ClientCapabilities): void { }
initialize(capabilities: ServerCapabilities): void { }
protected registerProvider(options: any): Disposable { return undefined; }
private constructor(context: AppContext, protected readonly client: SqlOpsDataClient) {
super(client, SqlCredentialService.messagesTypes);
}
}