Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/authentication/browser/authenticationUsageService.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Queue } from '../../../../base/common/async.js';
7
import { Disposable, toDisposable } from '../../../../base/common/lifecycle.js';
8
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
9
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
10
import { ILogService } from '../../../../platform/log/common/log.js';
11
import { IProductService } from '../../../../platform/product/common/productService.js';
12
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
13
import { IAuthenticationService } from '../common/authentication.js';
14
15
export interface IAccountUsage {
16
extensionId: string;
17
extensionName: string;
18
lastUsed: number;
19
scopes?: string[];
20
}
21
22
export const IAuthenticationUsageService = createDecorator<IAuthenticationUsageService>('IAuthenticationUsageService');
23
export interface IAuthenticationUsageService {
24
readonly _serviceBrand: undefined;
25
/**
26
* Initializes the cache of extensions that use authentication. Ideally used in a contribution that can be run eventually after the workspace is loaded.
27
*/
28
initializeExtensionUsageCache(): Promise<void>;
29
/**
30
* Checks if an extension uses authentication
31
* @param extensionId The id of the extension to check
32
*/
33
extensionUsesAuth(extensionId: string): Promise<boolean>;
34
/**
35
* Reads the usages for an account
36
* @param providerId The id of the authentication provider to get usages for
37
* @param accountName The name of the account to get usages for
38
*/
39
readAccountUsages(providerId: string, accountName: string,): IAccountUsage[];
40
/**
41
*
42
* @param providerId The id of the authentication provider to get usages for
43
* @param accountName The name of the account to get usages for
44
*/
45
removeAccountUsage(providerId: string, accountName: string): void;
46
/**
47
* Adds a usage for an account
48
* @param providerId The id of the authentication provider to get usages for
49
* @param accountName The name of the account to get usages for
50
* @param extensionId The id of the extension to add a usage for
51
* @param extensionName The name of the extension to add a usage for
52
*/
53
addAccountUsage(providerId: string, accountName: string, scopes: ReadonlyArray<string> | undefined, extensionId: string, extensionName: string): void;
54
}
55
56
export class AuthenticationUsageService extends Disposable implements IAuthenticationUsageService {
57
_serviceBrand: undefined;
58
59
private _queue = this._register(new Queue());
60
private _extensionsUsingAuth = new Set<string>();
61
62
private _disposed = false;
63
64
constructor(
65
@IStorageService private readonly _storageService: IStorageService,
66
@IAuthenticationService private readonly _authenticationService: IAuthenticationService,
67
@ILogService private readonly _logService: ILogService,
68
@IProductService productService: IProductService,
69
) {
70
super();
71
this._register(toDisposable(() => this._disposed = true));
72
// If an extension is listed in `trustedExtensionAuthAccess` we should consider it as using auth
73
const trustedExtensionAuthAccess = productService.trustedExtensionAuthAccess;
74
if (Array.isArray(trustedExtensionAuthAccess)) {
75
for (const extensionId of trustedExtensionAuthAccess) {
76
this._extensionsUsingAuth.add(extensionId);
77
}
78
} else if (trustedExtensionAuthAccess) {
79
for (const extensions of Object.values(trustedExtensionAuthAccess)) {
80
for (const extensionId of extensions) {
81
this._extensionsUsingAuth.add(extensionId);
82
}
83
}
84
}
85
86
this._register(this._authenticationService.onDidRegisterAuthenticationProvider(
87
provider => this._queue.queue(
88
() => this._addExtensionsToCache(provider.id)
89
)
90
));
91
}
92
93
async initializeExtensionUsageCache(): Promise<void> {
94
await this._queue.queue(() => Promise.all(this._authenticationService.getProviderIds().map(providerId => this._addExtensionsToCache(providerId))));
95
}
96
97
async extensionUsesAuth(extensionId: string): Promise<boolean> {
98
await this._queue.whenIdle();
99
return this._extensionsUsingAuth.has(extensionId);
100
}
101
102
readAccountUsages(providerId: string, accountName: string): IAccountUsage[] {
103
const accountKey = `${providerId}-${accountName}-usages`;
104
const storedUsages = this._storageService.get(accountKey, StorageScope.APPLICATION);
105
let usages: IAccountUsage[] = [];
106
if (storedUsages) {
107
try {
108
usages = JSON.parse(storedUsages);
109
} catch (e) {
110
// ignore
111
}
112
}
113
114
return usages;
115
}
116
117
removeAccountUsage(providerId: string, accountName: string): void {
118
const accountKey = `${providerId}-${accountName}-usages`;
119
this._storageService.remove(accountKey, StorageScope.APPLICATION);
120
}
121
122
addAccountUsage(providerId: string, accountName: string, scopes: string[] | undefined, extensionId: string, extensionName: string): void {
123
const accountKey = `${providerId}-${accountName}-usages`;
124
const usages = this.readAccountUsages(providerId, accountName);
125
126
const existingUsageIndex = usages.findIndex(usage => usage.extensionId === extensionId);
127
if (existingUsageIndex > -1) {
128
usages.splice(existingUsageIndex, 1, {
129
extensionId,
130
extensionName,
131
scopes,
132
lastUsed: Date.now()
133
});
134
} else {
135
usages.push({
136
extensionId,
137
extensionName,
138
scopes,
139
lastUsed: Date.now()
140
});
141
}
142
143
this._storageService.store(accountKey, JSON.stringify(usages), StorageScope.APPLICATION, StorageTarget.MACHINE);
144
this._extensionsUsingAuth.add(extensionId);
145
}
146
147
private async _addExtensionsToCache(providerId: string) {
148
if (this._disposed) {
149
return;
150
}
151
try {
152
const accounts = await this._authenticationService.getAccounts(providerId);
153
for (const account of accounts) {
154
const usage = this.readAccountUsages(providerId, account.label);
155
for (const u of usage) {
156
this._extensionsUsingAuth.add(u.extensionId);
157
}
158
}
159
} catch (e) {
160
this._logService.error(e);
161
}
162
}
163
}
164
165
registerSingleton(IAuthenticationUsageService, AuthenticationUsageService, InstantiationType.Delayed);
166
167