Skip to content

Commit 3aed3ad

Browse files
authored
Add command to sign out of multiple accounts at once (#288)
1 parent 617c0cd commit 3aed3ad

File tree

3 files changed

+62
-4
lines changed

3 files changed

+62
-4
lines changed

package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,11 @@
375375
"command": "intersystems-community.servermanager.viewWebApp",
376376
"title": "View Files in Web Application",
377377
"icon": "$(eye)"
378+
},
379+
{
380+
"command": "intersystems-community.servermanager.signOut",
381+
"category": "InterSystems Server Manager",
382+
"title": "Sign Out of Accounts..."
378383
}
379384
],
380385
"submenus": [
@@ -541,6 +546,11 @@
541546
"when": "view == intersystems-community_servermanager",
542547
"group": "1_edit"
543548
},
549+
{
550+
"command": "intersystems-community.servermanager.signOut",
551+
"when": "view == intersystems-community_servermanager",
552+
"group": "1_edit"
553+
},
544554
{
545555
"command": "intersystems-community.servermanager.importServers",
546556
"when": "view == intersystems-community_servermanager && isWindows",

src/authenticationProvider.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,6 @@ export class ServerManagerAuthenticationProvider implements AuthenticationProvid
165165
const enteredPassword = inputBox.value;
166166
if (secretStorage && enteredPassword) {
167167
await secretStorage.store(credentialKey, enteredPassword);
168-
console.log(`Stored password at ${credentialKey}`);
169168
}
170169
// Resolve the promise and tidy up
171170
resolve(enteredPassword);
@@ -270,7 +269,6 @@ export class ServerManagerAuthenticationProvider implements AuthenticationProvid
270269
if (deletePassword) {
271270
// Delete from secret storage
272271
await this.secretStorage.delete(credentialKey);
273-
console.log(`${AUTHENTICATION_PROVIDER_LABEL}: Deleted password at ${credentialKey}`);
274272
}
275273
if (index > -1) {
276274
// Remove session here so we don't store it
@@ -280,6 +278,45 @@ export class ServerManagerAuthenticationProvider implements AuthenticationProvid
280278
this._onDidChangeSessions.fire({ added: [], removed: [session], changed: [] });
281279
}
282280

281+
public async removeSessions(sessionIds: string[]): Promise<void> {
282+
const storedPasswordCredKeys: string[] = [];
283+
const removed: AuthenticationSession[] = [];
284+
await Promise.allSettled(sessionIds.map(async (sessionId) => {
285+
const index = this._sessions.findIndex((item) => item.id === sessionId);
286+
const session = this._sessions[index];
287+
const credentialKey = ServerManagerAuthenticationProvider.credentialKey(sessionId);
288+
if (await this.secretStorage.get(credentialKey) !== undefined) {
289+
storedPasswordCredKeys.push(credentialKey);
290+
}
291+
if (index > -1) {
292+
this._sessions.splice(index, 1);
293+
}
294+
removed.push(session);
295+
}));
296+
if (storedPasswordCredKeys.length) {
297+
const passwordOption = workspace.getConfiguration("intersystemsServerManager.credentialsProvider")
298+
.get<string>("deletePasswordOnSignout", "ask");
299+
let deletePasswords = (passwordOption === "always");
300+
if (passwordOption === "ask") {
301+
const choice = await window.showWarningMessage(
302+
`Do you want to keep the stored passwords or delete them?`,
303+
{
304+
detail: `${storedPasswordCredKeys.length == sessionIds.length ? "All" : "Some"
305+
} of the ${AUTHENTICATION_PROVIDER_LABEL} accounts you signed out are currently storing their passwords securely on your workstation.`, modal: true
306+
},
307+
{ title: "Keep", isCloseAffordance: true },
308+
{ title: "Delete", isCloseAffordance: false },
309+
);
310+
deletePasswords = (choice?.title === "Delete");
311+
}
312+
if (deletePasswords) {
313+
await Promise.allSettled(storedPasswordCredKeys.map((e) => this.secretStorage.delete(e)));
314+
}
315+
}
316+
await this._storeStrippedSessions();
317+
this._onDidChangeSessions.fire({ added: [], removed, changed: [] });
318+
}
319+
283320
private async _ensureInitialized(): Promise<void> {
284321
if (this._initializedDisposable === undefined) {
285322

src/commonActivate.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,14 @@ export function commonActivate(context: vscode.ExtensionContext, view: ServerMan
7676
}
7777
};
7878

79+
const authProvider = new ServerManagerAuthenticationProvider(context.secrets);
7980
context.subscriptions.push(
8081
// Register our authentication provider. NOTE: this will register the provider globally which means that
8182
// any other extension can request to use use this provider via the `vscode.authentication.getSession` API.
8283
vscode.authentication.registerAuthenticationProvider(
8384
ServerManagerAuthenticationProvider.id,
8485
ServerManagerAuthenticationProvider.label,
85-
new ServerManagerAuthenticationProvider(context.secrets),
86+
authProvider,
8687
{ supportsMultipleAccounts: true },
8788
),
8889
// Ensure cookies do not survive an account sign-out
@@ -136,7 +137,7 @@ export function commonActivate(context: vscode.ExtensionContext, view: ServerMan
136137
}
137138
const choice = await vscode.window.showQuickPick(options, {
138139
ignoreFocusOut: true,
139-
title: "Pick a settngs scope in which to add the server definition"
140+
title: "Pick a settings scope in which to add the server definition"
140141
});
141142
if (!choice) return;
142143
scope = choice.scope;
@@ -330,6 +331,16 @@ export function commonActivate(context: vscode.ExtensionContext, view: ServerMan
330331
await addWorkspaceFolderAsync(true, true, <NamespaceTreeItem>webAppTreeItem?.parent?.parent, undefined, webAppTreeItem?.name);
331332
}),
332333
vscode.workspace.onDidChangeWorkspaceFolders(() => view.refreshTree()),
334+
vscode.commands.registerCommand(`${extensionId}.signOut`, async () => {
335+
const sessions = await authProvider.getSessions(undefined, {}).catch(() => { });
336+
if (!sessions?.length) {
337+
vscode.window.showInformationMessage("There are no stored accounts to sign out of.", "Dismiss");
338+
return;
339+
}
340+
const picks = await vscode.window.showQuickPick(sessions.map((s) => s.account), { canPickMany: true, title: "Pick the accounts to sign out of" });
341+
if (!picks?.length) return;
342+
return authProvider.removeSessions(picks.map((p) => p.id));
343+
}),
333344
// Listen for relevant configuration changes
334345
vscode.workspace.onDidChangeConfiguration((e) => {
335346
if (e.affectsConfiguration("intersystems.servers") || e.affectsConfiguration("objectscript.conn")) {

0 commit comments

Comments
 (0)