@@ -12,11 +12,9 @@ import { Action2 } from '../../../../../platform/actions/common/actions.js';
12
12
import { ICommandService } from '../../../../../platform/commands/common/commands.js' ;
13
13
import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js' ;
14
14
import { IInstantiationService , ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js' ;
15
- import { IProductService } from '../../../../../platform/product/common/productService.js' ;
16
15
import { IQuickInputService , IQuickPickItem , IQuickPickSeparator } from '../../../../../platform/quickinput/common/quickInput.js' ;
17
- import { IAuthenticationAccessService } from '../../../../services/authentication/browser/authenticationAccessService.js' ;
18
- import { IAuthenticationUsageService } from '../../../../services/authentication/browser/authenticationUsageService.js' ;
19
16
import { AllowedExtension , IAuthenticationService } from '../../../../services/authentication/common/authentication.js' ;
17
+ import { IAuthenticationQueryService , IAccountQuery } from '../../../../services/authentication/common/authenticationQuery.js' ;
20
18
import { IExtensionService } from '../../../../services/extensions/common/extensions.js' ;
21
19
22
20
export class ManageTrustedExtensionsForAccountAction extends Action2 {
@@ -42,133 +40,110 @@ interface TrustedExtensionsQuickPickItem extends IQuickPickItem {
42
40
43
41
class ManageTrustedExtensionsForAccountActionImpl {
44
42
constructor (
45
- @IProductService private readonly _productService : IProductService ,
46
43
@IExtensionService private readonly _extensionService : IExtensionService ,
47
44
@IDialogService private readonly _dialogService : IDialogService ,
48
45
@IQuickInputService private readonly _quickInputService : IQuickInputService ,
49
46
@IAuthenticationService private readonly _authenticationService : IAuthenticationService ,
50
- @IAuthenticationUsageService private readonly _authenticationUsageService : IAuthenticationUsageService ,
51
- @IAuthenticationAccessService private readonly _authenticationAccessService : IAuthenticationAccessService ,
47
+ @IAuthenticationQueryService private readonly _authenticationQueryService : IAuthenticationQueryService ,
52
48
@ICommandService private readonly _commandService : ICommandService
53
49
) { }
54
50
55
51
async run ( options ?: { providerId : string ; accountLabel : string } ) {
56
- const { providerId , accountLabel } = await this . _resolveProviderAndAccountLabel ( options ?. providerId , options ?. accountLabel ) ;
57
- if ( ! providerId || ! accountLabel ) {
52
+ const accountQuery = await this . _resolveAccountQuery ( options ?. providerId , options ?. accountLabel ) ;
53
+ if ( ! accountQuery ) {
58
54
return ;
59
55
}
60
56
61
- const items = await this . _getItems ( providerId , accountLabel ) ;
57
+ const items = await this . _getItems ( accountQuery ) ;
62
58
if ( ! items . length ) {
63
59
return ;
64
60
}
65
- const disposables = new DisposableStore ( ) ;
66
- const picker = this . _createQuickPick ( disposables , providerId , accountLabel ) ;
61
+ const picker = this . _createQuickPick ( accountQuery ) ;
67
62
picker . items = items ;
68
63
picker . selectedItems = items . filter ( ( i ) : i is TrustedExtensionsQuickPickItem => i . type !== 'separator' && ! ! i . picked ) ;
69
64
picker . show ( ) ;
70
65
}
71
66
72
- private async _resolveProviderAndAccountLabel ( providerId : string | undefined , accountLabel : string | undefined ) {
73
- if ( ! providerId || ! accountLabel ) {
74
- const accounts = new Array < { providerId : string ; providerLabel : string ; accountLabel : string } > ( ) ;
75
- for ( const id of this . _authenticationService . getProviderIds ( ) ) {
76
- const providerLabel = this . _authenticationService . getProvider ( id ) . label ;
77
- const sessions = await this . _authenticationService . getSessions ( id ) ;
78
- const uniqueAccountLabels = new Set < string > ( ) ;
79
- for ( const session of sessions ) {
80
- if ( ! uniqueAccountLabels . has ( session . account . label ) ) {
81
- uniqueAccountLabels . add ( session . account . label ) ;
82
- accounts . push ( { providerId : id , providerLabel, accountLabel : session . account . label } ) ;
83
- }
84
- }
85
- }
67
+ //#region Account Query Resolution
86
68
87
- const pick = await this . _quickInputService . pick (
88
- accounts . map ( account => ( {
89
- providerId : account . providerId ,
90
- label : account . accountLabel ,
91
- description : account . providerLabel
92
- } ) ) ,
93
- {
94
- placeHolder : localize ( 'pickAccount' , "Pick an account to manage trusted extensions for" ) ,
95
- matchOnDescription : true ,
96
- }
97
- ) ;
98
-
99
- if ( pick ) {
100
- providerId = pick . providerId ;
101
- accountLabel = pick . label ;
102
- } else {
103
- return { providerId : undefined , accountLabel : undefined } ;
104
- }
69
+ private async _resolveAccountQuery ( providerId : string | undefined , accountLabel : string | undefined ) : Promise < IAccountQuery | undefined > {
70
+ if ( providerId && accountLabel ) {
71
+ return this . _authenticationQueryService . provider ( providerId ) . account ( accountLabel ) ;
105
72
}
106
- return { providerId, accountLabel } ;
73
+
74
+ const accounts = await this . _getAllAvailableAccounts ( ) ;
75
+ const pick = await this . _quickInputService . pick ( accounts , {
76
+ placeHolder : localize ( 'pickAccount' , "Pick an account to manage trusted extensions for" ) ,
77
+ matchOnDescription : true ,
78
+ } ) ;
79
+
80
+ return pick ? this . _authenticationQueryService . provider ( pick . providerId ) . account ( pick . label ) : undefined ;
107
81
}
108
82
109
- private async _getItems ( providerId : string , accountLabel : string ) {
110
- let allowedExtensions = this . _authenticationAccessService . readAllowedExtensions ( providerId , accountLabel ) ;
111
- // only include extensions that are installed
112
- const resolvedExtensions = await Promise . all ( allowedExtensions . map ( ext => this . _extensionService . getExtension ( ext . id ) ) ) ;
113
- allowedExtensions = resolvedExtensions
114
- . map ( ( ext , i ) => ext ? allowedExtensions [ i ] : undefined )
115
- . filter ( ext => ! ! ext ) ;
116
- const trustedExtensionAuthAccess = this . _productService . trustedExtensionAuthAccess ;
117
- const trustedExtensionIds =
118
- // Case 1: trustedExtensionAuthAccess is an array
119
- Array . isArray ( trustedExtensionAuthAccess )
120
- ? trustedExtensionAuthAccess
121
- // Case 2: trustedExtensionAuthAccess is an object
122
- : typeof trustedExtensionAuthAccess === 'object'
123
- ? trustedExtensionAuthAccess [ providerId ] ?? [ ]
124
- : [ ] ;
125
- for ( const extensionId of trustedExtensionIds ) {
126
- const allowedExtension = allowedExtensions . find ( ext => ext . id === extensionId ) ;
127
- if ( ! allowedExtension ) {
128
- // Add the extension to the allowedExtensions list
129
- const extension = await this . _extensionService . getExtension ( extensionId ) ;
130
- if ( extension ) {
131
- allowedExtensions . push ( {
132
- id : extensionId ,
133
- name : extension . displayName || extension . name ,
134
- allowed : true ,
135
- trusted : true
83
+ private async _getAllAvailableAccounts ( ) {
84
+ const accounts = [ ] ;
85
+ for ( const providerId of this . _authenticationService . getProviderIds ( ) ) {
86
+ const provider = this . _authenticationService . getProvider ( providerId ) ;
87
+ const sessions = await this . _authenticationService . getSessions ( providerId ) ;
88
+ const uniqueLabels = new Set < string > ( ) ;
89
+
90
+ for ( const session of sessions ) {
91
+ if ( ! uniqueLabels . has ( session . account . label ) ) {
92
+ uniqueLabels . add ( session . account . label ) ;
93
+ accounts . push ( {
94
+ providerId,
95
+ label : session . account . label ,
96
+ description : provider . label
136
97
} ) ;
137
98
}
138
- } else {
139
- // Update the extension to be allowed
140
- allowedExtension . allowed = true ;
141
- allowedExtension . trusted = true ;
142
99
}
143
100
}
101
+ return accounts ;
102
+ }
144
103
145
- if ( ! allowedExtensions . length ) {
146
- this . _dialogService . info ( localize ( 'noTrustedExtensions' , "This account has not been used by any extensions." ) ) ;
147
- return [ ] ;
148
- }
104
+ //#endregion
149
105
150
- const usages = this . _authenticationUsageService . readAccountUsages ( providerId , accountLabel ) ;
151
- const trustedExtensions = [ ] ;
152
- const otherExtensions = [ ] ;
153
- for ( const extension of allowedExtensions ) {
154
- const usage = usages . find ( usage => extension . id === usage . extensionId ) ;
155
- extension . lastUsed = usage ?. lastUsed ;
156
- if ( extension . trusted ) {
157
- trustedExtensions . push ( extension ) ;
158
- } else {
159
- otherExtensions . push ( extension ) ;
106
+ //#region Item Retrieval and Quick Pick Creation
107
+
108
+ private async _getItems ( accountQuery : IAccountQuery ) {
109
+ const allowedExtensions = accountQuery . extensions ( ) . getAllowedExtensions ( ) ;
110
+ const extensionIdToDisplayName = new Map < string , string > ( ) ;
111
+
112
+ // Get display names for all allowed extensions
113
+ const resolvedExtensions = await Promise . all ( allowedExtensions . map ( ext => this . _extensionService . getExtension ( ext . id ) ) ) ;
114
+ resolvedExtensions . forEach ( ( resolved , i ) => {
115
+ if ( resolved ) {
116
+ extensionIdToDisplayName . set ( allowedExtensions [ i ] . id , resolved . displayName || resolved . name ) ;
160
117
}
118
+ } ) ;
119
+
120
+ // Filter out extensions that are not currently installed and enrich with display names
121
+ const filteredExtensions = allowedExtensions
122
+ . filter ( ext => extensionIdToDisplayName . has ( ext . id ) )
123
+ . map ( ext => {
124
+ const usage = accountQuery . extension ( ext . id ) . getUsage ( ) ;
125
+ return {
126
+ ...ext ,
127
+ // Use the extension display name from the extension service
128
+ name : extensionIdToDisplayName . get ( ext . id ) ! ,
129
+ lastUsed : usage . length > 0 ? Math . max ( ...usage . map ( u => u . lastUsed ) ) : ext . lastUsed
130
+ } ;
131
+ } ) ;
132
+
133
+ if ( ! filteredExtensions . length ) {
134
+ this . _dialogService . info ( localize ( 'noTrustedExtensions' , "This account has not been used by any extensions." ) ) ;
135
+ return [ ] ;
161
136
}
162
137
138
+ const trustedExtensions = filteredExtensions . filter ( e => e . trusted ) ;
139
+ const otherExtensions = filteredExtensions . filter ( e => ! e . trusted ) ;
163
140
const sortByLastUsed = ( a : AllowedExtension , b : AllowedExtension ) => ( b . lastUsed || 0 ) - ( a . lastUsed || 0 ) ;
164
141
165
- const items = [
142
+ return [
166
143
...otherExtensions . sort ( sortByLastUsed ) . map ( this . _toQuickPickItem ) ,
167
144
{ type : 'separator' , label : localize ( 'trustedExtensions' , "Trusted by Microsoft" ) } satisfies IQuickPickSeparator ,
168
145
...trustedExtensions . sort ( sortByLastUsed ) . map ( this . _toQuickPickItem )
169
146
] ;
170
-
171
- return items ;
172
147
}
173
148
174
149
private _toQuickPickItem ( extension : AllowedExtension ) : TrustedExtensionsQuickPickItem {
@@ -196,38 +171,39 @@ class ManageTrustedExtensionsForAccountActionImpl {
196
171
} ;
197
172
}
198
173
199
- private _createQuickPick ( disposableStore : DisposableStore , providerId : string , accountLabel : string ) {
174
+ private _createQuickPick ( accountQuery : IAccountQuery ) {
175
+ const disposableStore = new DisposableStore ( ) ;
200
176
const quickPick = disposableStore . add ( this . _quickInputService . createQuickPick < TrustedExtensionsQuickPickItem > ( { useSeparators : true } ) ) ;
177
+
178
+ // Configure quick pick
201
179
quickPick . canSelectMany = true ;
202
180
quickPick . customButton = true ;
203
181
quickPick . customLabel = localize ( 'manageTrustedExtensions.cancel' , 'Cancel' ) ;
204
-
205
182
quickPick . title = localize ( 'manageTrustedExtensions' , "Manage Trusted Extensions" ) ;
206
183
quickPick . placeholder = localize ( 'manageExtensions' , "Choose which extensions can access this account" ) ;
207
184
185
+ // Set up event handlers
208
186
disposableStore . add ( quickPick . onDidAccept ( ( ) => {
209
187
const updatedAllowedList = quickPick . items
210
188
. filter ( ( item ) : item is TrustedExtensionsQuickPickItem => item . type !== 'separator' )
211
189
. map ( i => i . extension ) ;
212
190
213
191
const allowedExtensionsSet = new Set ( quickPick . selectedItems . map ( i => i . extension ) ) ;
214
- updatedAllowedList . forEach ( extension => {
215
- extension . allowed = allowedExtensionsSet . has ( extension ) ;
216
- } ) ;
217
- this . _authenticationAccessService . updateAllowedExtensions ( providerId , accountLabel , updatedAllowedList ) ;
192
+ for ( const extension of updatedAllowedList ) {
193
+ const allowed = allowedExtensionsSet . has ( extension ) ;
194
+ accountQuery . extension ( extension . id ) . setAccessAllowed ( allowed , extension . name ) ;
195
+ }
218
196
quickPick . hide ( ) ;
219
197
} ) ) ;
220
198
221
- disposableStore . add ( quickPick . onDidHide ( ( ) => {
222
- disposableStore . dispose ( ) ;
223
- } ) ) ;
224
-
225
- disposableStore . add ( quickPick . onDidCustom ( ( ) => {
226
- quickPick . hide ( ) ;
227
- } ) ) ;
199
+ disposableStore . add ( quickPick . onDidHide ( ( ) => disposableStore . dispose ( ) ) ) ;
200
+ disposableStore . add ( quickPick . onDidCustom ( ( ) => quickPick . hide ( ) ) ) ;
228
201
disposableStore . add ( quickPick . onDidTriggerItemButton ( e =>
229
- this . _commandService . executeCommand ( '_manageAccountPreferencesForExtension' , e . item . extension . id , providerId )
202
+ this . _commandService . executeCommand ( '_manageAccountPreferencesForExtension' , e . item . extension . id , accountQuery . providerId )
230
203
) ) ;
204
+
231
205
return quickPick ;
232
206
}
207
+
208
+ //#endregion
233
209
}
0 commit comments