@@ -12,9 +12,11 @@ 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' ;
15
16
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' ;
16
19
import { AllowedExtension , IAuthenticationService } from '../../../../services/authentication/common/authentication.js' ;
17
- import { IAuthenticationQueryService , IAccountQuery } from '../../../../services/authentication/common/authenticationQuery.js' ;
18
20
import { IExtensionService } from '../../../../services/extensions/common/extensions.js' ;
19
21
20
22
export class ManageTrustedExtensionsForAccountAction extends Action2 {
@@ -40,110 +42,133 @@ interface TrustedExtensionsQuickPickItem extends IQuickPickItem {
40
42
41
43
class ManageTrustedExtensionsForAccountActionImpl {
42
44
constructor (
45
+ @IProductService private readonly _productService : IProductService ,
43
46
@IExtensionService private readonly _extensionService : IExtensionService ,
44
47
@IDialogService private readonly _dialogService : IDialogService ,
45
48
@IQuickInputService private readonly _quickInputService : IQuickInputService ,
46
49
@IAuthenticationService private readonly _authenticationService : IAuthenticationService ,
47
- @IAuthenticationQueryService private readonly _authenticationQueryService : IAuthenticationQueryService ,
50
+ @IAuthenticationUsageService private readonly _authenticationUsageService : IAuthenticationUsageService ,
51
+ @IAuthenticationAccessService private readonly _authenticationAccessService : IAuthenticationAccessService ,
48
52
@ICommandService private readonly _commandService : ICommandService
49
53
) { }
50
54
51
55
async run ( options ?: { providerId : string ; accountLabel : string } ) {
52
- const accountQuery = await this . _resolveAccountQuery ( options ?. providerId , options ?. accountLabel ) ;
53
- if ( ! accountQuery ) {
56
+ const { providerId , accountLabel } = await this . _resolveProviderAndAccountLabel ( options ?. providerId , options ?. accountLabel ) ;
57
+ if ( ! providerId || ! accountLabel ) {
54
58
return ;
55
59
}
56
60
57
- const items = await this . _getItems ( accountQuery ) ;
61
+ const items = await this . _getItems ( providerId , accountLabel ) ;
58
62
if ( ! items . length ) {
59
63
return ;
60
64
}
61
- const picker = this . _createQuickPick ( accountQuery ) ;
65
+ const disposables = new DisposableStore ( ) ;
66
+ const picker = this . _createQuickPick ( disposables , providerId , accountLabel ) ;
62
67
picker . items = items ;
63
68
picker . selectedItems = items . filter ( ( i ) : i is TrustedExtensionsQuickPickItem => i . type !== 'separator' && ! ! i . picked ) ;
64
69
picker . show ( ) ;
65
70
}
66
71
67
- //#region Account Query Resolution
68
-
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 ) ;
72
- }
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 ;
81
- }
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
+ }
82
86
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
97
- } ) ;
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 ,
98
96
}
97
+ ) ;
98
+
99
+ if ( pick ) {
100
+ providerId = pick . providerId ;
101
+ accountLabel = pick . label ;
102
+ } else {
103
+ return { providerId : undefined , accountLabel : undefined } ;
99
104
}
100
105
}
101
- return accounts ;
106
+ return { providerId , accountLabel } ;
102
107
}
103
108
104
- //#endregion
105
-
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
109
+ private async _getItems ( providerId : string , accountLabel : string ) {
110
+ let allowedExtensions = this . _authenticationAccessService . readAllowedExtensions ( providerId , accountLabel ) ;
111
+ // only include extensions that are installed
113
112
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 ) ;
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
136
+ } ) ;
137
+ }
138
+ } else {
139
+ // Update the extension to be allowed
140
+ allowedExtension . allowed = true ;
141
+ allowedExtension . trusted = true ;
117
142
}
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
- } ) ;
143
+ }
132
144
133
- if ( ! filteredExtensions . length ) {
145
+ if ( ! allowedExtensions . length ) {
134
146
this . _dialogService . info ( localize ( 'noTrustedExtensions' , "This account has not been used by any extensions." ) ) ;
135
147
return [ ] ;
136
148
}
137
149
138
- const trustedExtensions = filteredExtensions . filter ( e => e . trusted ) ;
139
- const otherExtensions = filteredExtensions . filter ( e => ! e . trusted ) ;
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 ) ;
160
+ }
161
+ }
162
+
140
163
const sortByLastUsed = ( a : AllowedExtension , b : AllowedExtension ) => ( b . lastUsed || 0 ) - ( a . lastUsed || 0 ) ;
141
164
142
- return [
165
+ const items = [
143
166
...otherExtensions . sort ( sortByLastUsed ) . map ( this . _toQuickPickItem ) ,
144
167
{ type : 'separator' , label : localize ( 'trustedExtensions' , "Trusted by Microsoft" ) } satisfies IQuickPickSeparator ,
145
168
...trustedExtensions . sort ( sortByLastUsed ) . map ( this . _toQuickPickItem )
146
169
] ;
170
+
171
+ return items ;
147
172
}
148
173
149
174
private _toQuickPickItem ( extension : AllowedExtension ) : TrustedExtensionsQuickPickItem {
@@ -171,39 +196,38 @@ class ManageTrustedExtensionsForAccountActionImpl {
171
196
} ;
172
197
}
173
198
174
- private _createQuickPick ( accountQuery : IAccountQuery ) {
175
- const disposableStore = new DisposableStore ( ) ;
199
+ private _createQuickPick ( disposableStore : DisposableStore , providerId : string , accountLabel : string ) {
176
200
const quickPick = disposableStore . add ( this . _quickInputService . createQuickPick < TrustedExtensionsQuickPickItem > ( { useSeparators : true } ) ) ;
177
-
178
- // Configure quick pick
179
201
quickPick . canSelectMany = true ;
180
202
quickPick . customButton = true ;
181
203
quickPick . customLabel = localize ( 'manageTrustedExtensions.cancel' , 'Cancel' ) ;
204
+
182
205
quickPick . title = localize ( 'manageTrustedExtensions' , "Manage Trusted Extensions" ) ;
183
206
quickPick . placeholder = localize ( 'manageExtensions' , "Choose which extensions can access this account" ) ;
184
207
185
- // Set up event handlers
186
208
disposableStore . add ( quickPick . onDidAccept ( ( ) => {
187
209
const updatedAllowedList = quickPick . items
188
210
. filter ( ( item ) : item is TrustedExtensionsQuickPickItem => item . type !== 'separator' )
189
211
. map ( i => i . extension ) ;
190
212
191
213
const allowedExtensionsSet = new Set ( quickPick . selectedItems . map ( i => i . extension ) ) ;
192
- for ( const extension of updatedAllowedList ) {
193
- const allowed = allowedExtensionsSet . has ( extension ) ;
194
- accountQuery . extension ( extension . id ) . setAccessAllowed ( allowed , extension . name ) ;
195
- }
214
+ updatedAllowedList . forEach ( extension => {
215
+ extension . allowed = allowedExtensionsSet . has ( extension ) ;
216
+ } ) ;
217
+ this . _authenticationAccessService . updateAllowedExtensions ( providerId , accountLabel , updatedAllowedList ) ;
196
218
quickPick . hide ( ) ;
197
219
} ) ) ;
198
220
199
- disposableStore . add ( quickPick . onDidHide ( ( ) => disposableStore . dispose ( ) ) ) ;
200
- disposableStore . add ( quickPick . onDidCustom ( ( ) => quickPick . hide ( ) ) ) ;
221
+ disposableStore . add ( quickPick . onDidHide ( ( ) => {
222
+ disposableStore . dispose ( ) ;
223
+ } ) ) ;
224
+
225
+ disposableStore . add ( quickPick . onDidCustom ( ( ) => {
226
+ quickPick . hide ( ) ;
227
+ } ) ) ;
201
228
disposableStore . add ( quickPick . onDidTriggerItemButton ( e =>
202
- this . _commandService . executeCommand ( '_manageAccountPreferencesForExtension' , e . item . extension . id , accountQuery . providerId )
229
+ this . _commandService . executeCommand ( '_manageAccountPreferencesForExtension' , e . item . extension . id , providerId )
203
230
) ) ;
204
-
205
231
return quickPick ;
206
232
}
207
-
208
- //#endregion
209
233
}
0 commit comments