Skip to content

Commit 3a0a618

Browse files
committed
WIP
1 parent 0d35305 commit 3a0a618

File tree

3 files changed

+87
-65
lines changed

3 files changed

+87
-65
lines changed

package.json

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -67,23 +67,23 @@
6767
},
6868
"main": "./out/extension",
6969
"activationEvents": [
70-
"onAuthenticationRequest:intersystems-servermanager",
70+
"onAuthenticationRequest:intersystems-servermanager-credentials",
7171
"onView:intersystems-community_servermanager",
7272
"onCommand:intersystems-community.servermanager.refreshTree",
7373
"onCommand:intersystems-community.servermanager.addServer",
7474
"onCommand:intersystems-community.servermanager.storePassword",
7575
"onCommand:intersystems-community.servermanager.clearPassword",
7676
"onCommand:intersystems-community.servermanager.importServers",
77-
"onCommand:intersystems-community.servermanager.testLogin",
78-
"onCommand:intersystems-community.servermanager.testScopedLogin",
79-
"onCommand:intersystems-community.servermanager.testScopedLogin2",
80-
"onCommand:intersystems-community.servermanager.testScopedLoginWithPreferenceCleared",
81-
"onCommand:intersystems-community.servermanager.testScopedLogin2WithPreferenceCleared",
82-
"onCommand:intersystems-community.servermanager.testPartiallyScopedLogin",
83-
"onCommand:intersystems-community.servermanager.testPartiallyScopedLoginWithPreferenceCleared",
84-
"onCommand:intersystems-community.servermanager.testQuietLogin",
85-
"onCommand:intersystems-community.servermanager.testScopedQuietLogin",
86-
"onCommand:intersystems-community.servermanager.testScopedQuietLogin2"
77+
"onCommand:intersystems-community.servermanager-credentials.testLogin",
78+
"onCommand:intersystems-community.servermanager-credentials.testScopedLogin",
79+
"onCommand:intersystems-community.servermanager-credentials.testScopedLogin2",
80+
"onCommand:intersystems-community.servermanager-credentials.testScopedLoginWithPreferenceCleared",
81+
"onCommand:intersystems-community.servermanager-credentials.testScopedLogin2WithPreferenceCleared",
82+
"onCommand:intersystems-community.servermanager-credentials.testPartiallyScopedLogin",
83+
"onCommand:intersystems-community.servermanager-credentials.testPartiallyScopedLoginWithPreferenceCleared",
84+
"onCommand:intersystems-community.servermanager-credentials.testQuietLogin",
85+
"onCommand:intersystems-community.servermanager-credentials.testScopedQuietLogin",
86+
"onCommand:intersystems-community.servermanager-credentials.testScopedQuietLogin2"
8787
],
8888
"contributes": {
8989
"viewsContainers": {
@@ -230,6 +230,11 @@
230230
}
231231
},
232232
"additionalProperties": false
233+
},
234+
"intersystemsServerManager.useAuthenticationProvider": {
235+
"type": "boolean",
236+
"markdownDescription": "Use the 'intersystems-server-credentials' authentication provider for password management. Extensions that use the getServerSpec API will no longer receive a password in the response object. Instead they should call [`vscode.authentication`](https://code.visualstudio.com/api/references/vscode-api#authentication)`.getSession('intersystems-server-credentials', [serverName, userName])`. The `accessToken` property of the [`AuthenticationSession`](https://code.visualstudio.com/api/references/vscode-api#AuthenticationSession) promise returned is the password. If `serverName` or `userName` is omitted the user will be prompted.",
237+
"scope": "application"
233238
}
234239
}
235240
},
@@ -294,53 +299,53 @@
294299
"title": "Import Servers from Windows Registry"
295300
},
296301
{
297-
"command": "intersystems-community.servermanager.testLogin",
298-
"category": "InterSystems Server Manager",
302+
"command": "intersystems-community.servermanager-credentials.testLogin",
303+
"category": "InterSystems Server Credentials",
299304
"title": "Test Login"
300305
},
301306
{
302-
"command": "intersystems-community.servermanager.testScopedLogin",
303-
"category": "InterSystems Server Manager",
307+
"command": "intersystems-community.servermanager-credentials.testScopedLogin",
308+
"category": "InterSystems Server Credentials",
304309
"title": "Test Scoped Login (iris201 as johnm)"
305310
},
306311
{
307-
"command": "intersystems-community.servermanager.testScopedLogin2",
308-
"category": "InterSystems Server Manager",
312+
"command": "intersystems-community.servermanager-credentials.testScopedLogin2",
313+
"category": "InterSystems Server Credentials",
309314
"title": "Test Scoped Login (iris201 as johnm2)"
310315
},
311316
{
312-
"command": "intersystems-community.servermanager.testScopedLoginWithPreferenceCleared",
313-
"category": "InterSystems Server Manager",
317+
"command": "intersystems-community.servermanager-credentials.testScopedLoginWithPreferenceCleared",
318+
"category": "InterSystems Server Credentials",
314319
"title": "Test Scoped Login (iris201 as johnm) With Preference Cleared"
315320
},
316321
{
317-
"command": "intersystems-community.servermanager.testScopedLogin2WithPreferenceCleared",
318-
"category": "InterSystems Server Manager",
322+
"command": "intersystems-community.servermanager-credentials.testScopedLogin2WithPreferenceCleared",
323+
"category": "InterSystems Server Credentials",
319324
"title": "Test Scoped Login (iris201 as johnm2) With Preference Cleared"
320325
},
321326
{
322-
"command": "intersystems-community.servermanager.testPartiallyScopedLogin",
323-
"category": "InterSystems Server Manager",
327+
"command": "intersystems-community.servermanager-credentials.testPartiallyScopedLogin",
328+
"category": "InterSystems Server Credentials",
324329
"title": "Test Partially Scoped Login (iris201)"
325330
},
326331
{
327-
"command": "intersystems-community.servermanager.testPartiallyScopedLoginWithPreferenceCleared",
328-
"category": "InterSystems Server Manager",
332+
"command": "intersystems-community.servermanager-credentials.testPartiallyScopedLoginWithPreferenceCleared",
333+
"category": "InterSystems Server Credentials",
329334
"title": "Test Partially Scoped Login (iris201) With Preference Cleared"
330335
},
331336
{
332-
"command": "intersystems-community.servermanager.testQuietLogin",
333-
"category": "InterSystems Server Manager",
337+
"command": "intersystems-community.servermanager-credentials.testQuietLogin",
338+
"category": "InterSystems Server Credentials",
334339
"title": "Test Quiet Login"
335340
},
336341
{
337-
"command": "intersystems-community.servermanager.testScopedQuietLogin",
338-
"category": "InterSystems Server Manager",
342+
"command": "intersystems-community.servermanager-credentials.testScopedQuietLogin",
343+
"category": "InterSystems Server Credentials",
339344
"title": "Test Scoped Quiet Login"
340345
},
341346
{
342-
"command": "intersystems-community.servermanager.testScopedQuietLogin2",
343-
"category": "InterSystems Server Manager",
347+
"command": "intersystems-community.servermanager-credentials.testScopedQuietLogin2",
348+
"category": "InterSystems Server Credentials",
344349
"title": "Test Scoped Quiet Login2"
345350
},
346351
{

src/authenticationProvider.ts

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import {
77
Disposable,
88
Event,
99
EventEmitter,
10+
extensions,
1011
SecretStorage,
1112
ThemeIcon,
1213
window,
1314
} from 'vscode';
14-
import { pickServer } from './api/pickServer';
15+
16+
export const AUTHENTICATIONPROVIDER = "intersystems-server-credentials";
1517

1618
class ServerManagerAuthenticationSession implements AuthenticationSession {
1719
public readonly id: string;
@@ -31,8 +33,8 @@ class ServerManagerAuthenticationSession implements AuthenticationSession {
3133
}
3234

3335
export class ServerManagerAuthenticationProvider implements AuthenticationProvider, Disposable {
34-
static id = 'intersystems-servermanager';
35-
static label = 'InterSystems Server Manager';
36+
static id = AUTHENTICATIONPROVIDER;
37+
static label = 'InterSystems Server Credentials';
3638
static secretKeyPrefix = 'authenticationProvider:';
3739
static sessionId(serverName: string, userName: string): string {
3840
return `${serverName}@${userName}`;
@@ -45,12 +47,14 @@ export class ServerManagerAuthenticationProvider implements AuthenticationProvid
4547

4648
private _sessions: ServerManagerAuthenticationSession[] = [];
4749

50+
private _serverManagerExtension = extensions.getExtension('intersystems-community.servermanager');
51+
4852
private _onDidChangeSessions = new EventEmitter<AuthenticationProviderAuthenticationSessionsChangeEvent>();
4953
get onDidChangeSessions(): Event<AuthenticationProviderAuthenticationSessionsChangeEvent> {
5054
return this._onDidChangeSessions.event;
5155
}
5256

53-
constructor(private readonly secretStorage: SecretStorage) { }
57+
constructor(private readonly secretStorage: SecretStorage) {}
5458

5559
dispose(): void {
5660
this._initializedDisposable?.dispose();
@@ -102,7 +106,7 @@ export class ServerManagerAuthenticationProvider implements AuthenticationProvid
102106
}
103107

104108
// This function is called after `this.getSessions` is called, and only when:
105-
// - `this.getSessions` returns nothing but `createIfNone` was set to `true` in `vscode.authentication.getSession`
109+
// - `this.getSessions` returns nothing but `createIfNone` was set to `true` in client's call to `vscode.authentication.getSession`
106110
// - `vscode.authentication.getSession` was called with `forceNewSession: true` or `forceNewSession: "Reason message for modal dialog"` (proposed API since 1.59, finalized in 1.63)
107111
// - The end user initiates the "silent" auth flow via the Accounts menu
108112
async createSession(scopes: string[]): Promise<AuthenticationSession> {
@@ -111,7 +115,13 @@ export class ServerManagerAuthenticationProvider implements AuthenticationProvid
111115
let serverName = scopes[0] ?? '';
112116
if (!serverName) {
113117
// Prompt for the server name.
114-
serverName = await pickServer() ?? '';
118+
if (!this._serverManagerExtension) {
119+
throw new Error('InterSystems Server Manager extension is not available to provide server selection');
120+
}
121+
if (!this._serverManagerExtension.isActive) {
122+
await this._serverManagerExtension.activate();
123+
}
124+
serverName = await this._serverManagerExtension.exports.pickServer() ?? '';
115125
if (!serverName) {
116126
throw new Error('Server name is required');
117127
}
@@ -129,24 +139,31 @@ export class ServerManagerAuthenticationProvider implements AuthenticationProvid
129139
throw new Error('Username is required');
130140
}
131141
}
132-
const credentialKey = ServerManagerAuthenticationProvider.credentialKey(ServerManagerAuthenticationProvider.sessionId(serverName, userName));
133-
134-
let password = await this.secretStorage.get(credentialKey);
135142

143+
// Return existing session if found
144+
const sessionId = ServerManagerAuthenticationProvider.sessionId(serverName, userName);
145+
const existingSession = this._sessions.find((session) => session.id === sessionId);
146+
if (existingSession) {
147+
return existingSession;
148+
}
149+
150+
// Seek password in secret storage
151+
const credentialKey = ServerManagerAuthenticationProvider.credentialKey(sessionId);
152+
let password = await this.secretStorage.get(credentialKey);
136153
if (!password) {
137-
// Prompt for the password.
154+
// Prompt for password
138155
const doInputBox = async (): Promise<string | undefined> => {
139156
return await new Promise<string | undefined>((resolve, reject) => {
140157
const inputBox = window.createInputBox();
141158
inputBox.password = true,
142159
inputBox.title = `Password for InterSystems server '${serverName}'`,
143160
inputBox.placeholder = `Password for user '${userName}' on '${serverName}'`,
144-
inputBox.prompt = 'To store your password securely, submit it using the $(key) button',
161+
inputBox.prompt = 'Use $(key) button above to store password securely',
145162
inputBox.ignoreFocusOut = true,
146163
inputBox.buttons = [{ iconPath: new ThemeIcon('key'), tooltip: 'Store Password in Keychain' }]
147164

148165
async function done(secretStorage?: SecretStorage) {
149-
// File the password and return it
166+
// Return the password, having stored it if storage was passed
150167
const password = inputBox.value;
151168
if (secretStorage && password) {
152169
await secretStorage.store(credentialKey, password);
@@ -159,7 +176,7 @@ export class ServerManagerAuthenticationProvider implements AuthenticationProvid
159176
}
160177

161178
inputBox.onDidTriggerButton((_button) => {
162-
// We only added one button
179+
// We only added the one button, which stores the password
163180
done(this.secretStorage);
164181
});
165182
inputBox.onDidAccept(() => {

0 commit comments

Comments
 (0)