mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
allow policy config to compute the policy value for account based policies (#263410)
This commit is contained in:
committed by
GitHub
parent
0a565d144f
commit
8791650e81
@@ -0,0 +1,26 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export interface IDefaultAccount {
|
||||
readonly sessionId: string;
|
||||
readonly enterprise: boolean;
|
||||
readonly access_type_sku?: string;
|
||||
readonly assigned_date?: string;
|
||||
readonly can_signup_for_limited?: boolean;
|
||||
readonly chat_enabled?: boolean;
|
||||
readonly chat_preview_features_enabled?: boolean;
|
||||
readonly mcp?: boolean;
|
||||
readonly analytics_tracking_id?: string;
|
||||
readonly limited_user_quotas?: {
|
||||
readonly chat: number;
|
||||
readonly completions: number;
|
||||
};
|
||||
readonly monthly_quotas?: {
|
||||
readonly chat: number;
|
||||
readonly completions: number;
|
||||
};
|
||||
readonly limited_user_reset_date?: string;
|
||||
readonly chat_agent_enabled?: boolean;
|
||||
}
|
||||
@@ -3,14 +3,9 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export type PolicyName = string;
|
||||
import { IDefaultAccount } from './defaultAccount.js';
|
||||
|
||||
export enum PolicyTag {
|
||||
Account = 'ACCOUNT',
|
||||
MCP = 'MCP',
|
||||
Preview = 'PREVIEW',
|
||||
Agent = 'AGENT',
|
||||
}
|
||||
export type PolicyName = string;
|
||||
|
||||
export interface IPolicy {
|
||||
|
||||
@@ -36,18 +31,10 @@ export interface IPolicy {
|
||||
* this value determines what value the feature receives.
|
||||
*
|
||||
* For example:
|
||||
* - If `defaultValue: true`, the feature's setting is locked to `true` WHEN the policy is in effect.
|
||||
* - If `defaultValue: 'foo'`, the feature's setting is locked to 'foo' WHEN the policy is in effect.
|
||||
* - If evaluated value is `true`, the feature's setting is locked to `true` WHEN the policy is in effect.
|
||||
* - If evaluated value is `foo`, the feature's setting is locked to 'foo' WHEN the policy is in effect.
|
||||
*
|
||||
* If omitted, 'false' is the assumed value.
|
||||
*
|
||||
* Note: This is unrelated to the default value of the VS Code setting itself. This specifically controls
|
||||
* the value of an account-based feature's setting WHEN the policy is overriding it.
|
||||
* If `undefined`, the feature's setting is not locked and can be overridden by other means.
|
||||
*/
|
||||
readonly defaultValue?: string | number | boolean;
|
||||
|
||||
/**
|
||||
* Tags for categorizing policies
|
||||
*/
|
||||
readonly tags?: PolicyTag[];
|
||||
readonly value?: (account: IDefaultAccount) => string | number | boolean | undefined;
|
||||
}
|
||||
|
||||
@@ -137,12 +137,11 @@ export class PolicyConfiguration extends Disposable implements IPolicyConfigurat
|
||||
this.logService.warn(`Policy ${config.policy.name} has unsupported type ${config.type}`);
|
||||
continue;
|
||||
}
|
||||
const { defaultValue, tags } = config.policy;
|
||||
const { value } = config.policy;
|
||||
keys.push(key);
|
||||
policyDefinitions[config.policy.name] = {
|
||||
type: config.type === 'number' ? 'number' : config.type === 'boolean' ? 'boolean' : 'string',
|
||||
tags,
|
||||
defaultValue,
|
||||
value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,18 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IStringDictionary } from '../../../base/common/collections.js';
|
||||
import { IDefaultAccount } from '../../../base/common/defaultAccount.js';
|
||||
import { Emitter, Event } from '../../../base/common/event.js';
|
||||
import { Iterable } from '../../../base/common/iterator.js';
|
||||
import { Disposable } from '../../../base/common/lifecycle.js';
|
||||
import { PolicyName, PolicyTag } from '../../../base/common/policy.js';
|
||||
import { PolicyName } from '../../../base/common/policy.js';
|
||||
import { createDecorator } from '../../instantiation/common/instantiation.js';
|
||||
|
||||
export type PolicyValue = string | number | boolean;
|
||||
export type PolicyDefinition = { type: 'string' | 'number' | 'boolean'; defaultValue?: string | number | boolean; tags?: PolicyTag[] };
|
||||
export type PolicyDefinition = {
|
||||
type: 'string' | 'number' | 'boolean';
|
||||
value?: (account: IDefaultAccount) => string | number | boolean | undefined;
|
||||
};
|
||||
|
||||
export const IPolicyService = createDecorator<IPolicyService>('policy');
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import { MarkdownString, isMarkdownString } from '../../../../base/common/htmlCo
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import { Schemas } from '../../../../base/common/network.js';
|
||||
import { isMacintosh } from '../../../../base/common/platform.js';
|
||||
import { PolicyTag } from '../../../../base/common/policy.js';
|
||||
import { assertDefined } from '../../../../base/common/types.js';
|
||||
import { registerEditorFeature } from '../../../../editor/common/editorFeatures.js';
|
||||
import * as nls from '../../../../nls.js';
|
||||
@@ -245,8 +244,7 @@ configurationRegistry.registerConfiguration({
|
||||
policy: {
|
||||
name: 'ChatToolsAutoApprove',
|
||||
minimumVersion: '1.99',
|
||||
defaultValue: false,
|
||||
tags: [PolicyTag.Account, PolicyTag.Preview]
|
||||
value: (account) => account.chat_preview_features_enabled === false ? false : undefined,
|
||||
}
|
||||
},
|
||||
[ChatConfiguration.AutoApproveEdits]: {
|
||||
@@ -316,7 +314,7 @@ configurationRegistry.registerConfiguration({
|
||||
policy: {
|
||||
name: 'ChatMCP',
|
||||
minimumVersion: '1.99',
|
||||
tags: [PolicyTag.Account, PolicyTag.MCP]
|
||||
value: (account) => account.mcp === false ? false : undefined,
|
||||
}
|
||||
},
|
||||
[mcpAutoStartConfig]: {
|
||||
@@ -399,7 +397,7 @@ configurationRegistry.registerConfiguration({
|
||||
policy: {
|
||||
name: 'ChatAgentMode',
|
||||
minimumVersion: '1.99',
|
||||
tags: [PolicyTag.Account, PolicyTag.Agent]
|
||||
value: (account) => account.chat_agent_enabled === false ? false : undefined,
|
||||
}
|
||||
},
|
||||
[ChatConfiguration.EnableMath]: {
|
||||
|
||||
@@ -19,6 +19,7 @@ import { IWorkbenchContribution } from '../../../common/contributions.js';
|
||||
import { Barrier } from '../../../../base/common/async.js';
|
||||
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
|
||||
import { getErrorMessage } from '../../../../base/common/errors.js';
|
||||
import { IDefaultAccount } from '../../../../base/common/defaultAccount.js';
|
||||
|
||||
export const DEFAULT_ACCOUNT_SIGN_IN_COMMAND = 'workbench.actions.accounts.signIn';
|
||||
|
||||
@@ -30,28 +31,6 @@ const enum DefaultAccountStatus {
|
||||
|
||||
const CONTEXT_DEFAULT_ACCOUNT_STATE = new RawContextKey<string>('defaultAccountStatus', DefaultAccountStatus.Uninitialized);
|
||||
|
||||
export interface IDefaultAccount {
|
||||
readonly sessionId: string;
|
||||
readonly enterprise: boolean;
|
||||
readonly access_type_sku?: string;
|
||||
readonly assigned_date?: string;
|
||||
readonly can_signup_for_limited?: boolean;
|
||||
readonly chat_enabled?: boolean;
|
||||
readonly chat_preview_features_enabled?: boolean;
|
||||
readonly mcp?: boolean;
|
||||
readonly analytics_tracking_id?: string;
|
||||
readonly limited_user_quotas?: {
|
||||
readonly chat: number;
|
||||
readonly completions: number;
|
||||
};
|
||||
readonly monthly_quotas?: {
|
||||
readonly chat: number;
|
||||
readonly completions: number;
|
||||
};
|
||||
readonly limited_user_reset_date?: string;
|
||||
readonly chat_agent_enabled?: boolean;
|
||||
}
|
||||
|
||||
interface IChatEntitlementsResponse {
|
||||
readonly access_type_sku: string;
|
||||
readonly assigned_date: string;
|
||||
|
||||
+2
-1
@@ -20,10 +20,11 @@ import { IProductService } from '../../../../platform/product/common/productServ
|
||||
import { asJson, IRequestService } from '../../../../platform/request/common/request.js';
|
||||
import { IStorageService } from '../../../../platform/storage/common/storage.js';
|
||||
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
|
||||
import { IDefaultAccount, IDefaultAccountService } from '../../accounts/common/defaultAccount.js';
|
||||
import { IDefaultAccountService } from '../../accounts/common/defaultAccount.js';
|
||||
import { IRemoteAgentService } from '../../remote/common/remoteAgentService.js';
|
||||
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
|
||||
import { IHostService } from '../../host/browser/host.js';
|
||||
import { IDefaultAccount } from '../../../../base/common/defaultAccount.js';
|
||||
|
||||
export class WorkbenchExtensionGalleryManifestService extends ExtensionGalleryManifestService implements IExtensionGalleryManifestService {
|
||||
|
||||
|
||||
@@ -4,24 +4,16 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IStringDictionary } from '../../../../base/common/collections.js';
|
||||
import { equals } from '../../../../base/common/objects.js';
|
||||
import { PolicyTag } from '../../../../base/common/policy.js';
|
||||
import { IDefaultAccount } from '../../../../base/common/defaultAccount.js';
|
||||
import { ILogService } from '../../../../platform/log/common/log.js';
|
||||
import { AbstractPolicyService, IPolicyService, PolicyDefinition } from '../../../../platform/policy/common/policy.js';
|
||||
import { IDefaultAccountService } from '../../accounts/common/defaultAccount.js';
|
||||
|
||||
interface IAccountPolicy {
|
||||
readonly chatPreviewFeaturesEnabled: boolean;
|
||||
readonly mcpEnabled: boolean;
|
||||
readonly chatAgentEnabled: boolean;
|
||||
}
|
||||
|
||||
export class AccountPolicyService extends AbstractPolicyService implements IPolicyService {
|
||||
private accountPolicy: IAccountPolicy = {
|
||||
chatPreviewFeaturesEnabled: true,
|
||||
mcpEnabled: true,
|
||||
chatAgentEnabled: true
|
||||
};
|
||||
|
||||
private account: IDefaultAccount | null = null;
|
||||
|
||||
constructor(
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IDefaultAccountService private readonly defaultAccountService: IDefaultAccountService
|
||||
@@ -30,66 +22,32 @@ export class AccountPolicyService extends AbstractPolicyService implements IPoli
|
||||
|
||||
this.defaultAccountService.getDefaultAccount()
|
||||
.then(account => {
|
||||
this._update({
|
||||
chatPreviewFeaturesEnabled: account?.chat_preview_features_enabled ?? true,
|
||||
mcpEnabled: account?.mcp ?? true,
|
||||
chatAgentEnabled: account?.chat_agent_enabled ?? true
|
||||
});
|
||||
this._register(this.defaultAccountService.onDidChangeDefaultAccount(
|
||||
account => this._update({
|
||||
chatPreviewFeaturesEnabled: account?.chat_preview_features_enabled ?? true,
|
||||
mcpEnabled: account?.mcp ?? true,
|
||||
chatAgentEnabled: account?.chat_agent_enabled ?? true
|
||||
})
|
||||
));
|
||||
this.account = account;
|
||||
this._updatePolicyDefinitions(this.policyDefinitions);
|
||||
this._register(this.defaultAccountService.onDidChangeDefaultAccount(account => {
|
||||
this.account = account;
|
||||
this._updatePolicyDefinitions(this.policyDefinitions);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
private _update(updatedPolicy: IAccountPolicy): void {
|
||||
if (!equals(this.accountPolicy, updatedPolicy)) {
|
||||
this.accountPolicy = updatedPolicy;
|
||||
this._updatePolicyDefinitions(this.policyDefinitions);
|
||||
}
|
||||
}
|
||||
|
||||
protected async _updatePolicyDefinitions(policyDefinitions: IStringDictionary<PolicyDefinition>): Promise<void> {
|
||||
this.logService.trace(`AccountPolicyService#_updatePolicyDefinitions: Got ${Object.keys(policyDefinitions).length} policy definitions`);
|
||||
const updated: string[] = [];
|
||||
|
||||
const updateIfNeeded = (key: string, policy: PolicyDefinition, isFeatureEnabled: boolean): void => {
|
||||
if (isFeatureEnabled) {
|
||||
// Clear the policy if it is set
|
||||
for (const key in policyDefinitions) {
|
||||
const policy = policyDefinitions[key];
|
||||
const policyValue = this.account && policy.value ? policy.value(this.account) : undefined;
|
||||
if (policyValue !== undefined) {
|
||||
if (this.policies.get(key) !== policyValue) {
|
||||
this.policies.set(key, policyValue);
|
||||
updated.push(key);
|
||||
}
|
||||
} else {
|
||||
if (this.policies.has(key)) {
|
||||
this.policies.delete(key);
|
||||
updated.push(key);
|
||||
}
|
||||
} else {
|
||||
// Enforce the defaultValue if not already set
|
||||
const updatedValue = policy.defaultValue === undefined ? false : policy.defaultValue;
|
||||
if (this.policies.get(key) !== updatedValue) {
|
||||
this.policies.set(key, updatedValue);
|
||||
updated.push(key);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const hasAllTags = (policy: PolicyDefinition, tags: PolicyTag[]): boolean | undefined => {
|
||||
return policy.tags && tags.every(tag => policy.tags!.includes(tag));
|
||||
};
|
||||
|
||||
for (const key in policyDefinitions) {
|
||||
const policy = policyDefinitions[key];
|
||||
|
||||
// Map chat preview features with ACCOUNT + PREVIEW tags
|
||||
if (hasAllTags(policy, [PolicyTag.Account, PolicyTag.Preview])) {
|
||||
updateIfNeeded(key, policy, this.accountPolicy?.chatPreviewFeaturesEnabled);
|
||||
}
|
||||
// Map MCP feature with MCP tag
|
||||
else if (hasAllTags(policy, [PolicyTag.Account, PolicyTag.MCP])) {
|
||||
updateIfNeeded(key, policy, this.accountPolicy?.mcpEnabled);
|
||||
}
|
||||
else if (hasAllTags(policy, [PolicyTag.Account, PolicyTag.Agent])) {
|
||||
updateIfNeeded(key, policy, this.accountPolicy?.chatAgentEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import assert from 'assert';
|
||||
import { PolicyTag } from '../../../../../base/common/policy.js';
|
||||
import { NullLogService } from '../../../../../platform/log/common/log.js';
|
||||
import { DefaultAccountService, IDefaultAccount, IDefaultAccountService } from '../../../accounts/common/defaultAccount.js';
|
||||
import { DefaultAccountService, IDefaultAccountService } from '../../../accounts/common/defaultAccount.js';
|
||||
import { AccountPolicyService } from '../../common/accountPolicyService.js';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
|
||||
import { Registry } from '../../../../../platform/registry/common/platform.js';
|
||||
import { Extensions, IConfigurationNode, IConfigurationRegistry } from '../../../../../platform/configuration/common/configurationRegistry.js';
|
||||
import { DefaultConfiguration, PolicyConfiguration } from '../../../../../platform/configuration/common/configurations.js';
|
||||
import { IDefaultAccount } from '../../../../../base/common/defaultAccount.js';
|
||||
|
||||
const BASE_DEFAULT_ACCOUNT: IDefaultAccount = {
|
||||
enterprise: false,
|
||||
@@ -47,8 +47,7 @@ suite('AccountPolicyService', () => {
|
||||
policy: {
|
||||
name: 'PolicySettingB',
|
||||
minimumVersion: '1.0.0',
|
||||
defaultValue: "policyValueB",
|
||||
tags: [PolicyTag.Account, PolicyTag.Preview]
|
||||
value: account => account.chat_preview_features_enabled === false ? 'policyValueB' : undefined,
|
||||
}
|
||||
},
|
||||
'setting.C': {
|
||||
@@ -57,8 +56,7 @@ suite('AccountPolicyService', () => {
|
||||
policy: {
|
||||
name: 'PolicySettingC',
|
||||
minimumVersion: '1.0.0',
|
||||
defaultValue: JSON.stringify(['policyValueC1', 'policyValueC2']),
|
||||
tags: [PolicyTag.Account, PolicyTag.Preview]
|
||||
value: account => account.chat_preview_features_enabled === false ? JSON.stringify(['policyValueC1', 'policyValueC2']) : undefined,
|
||||
}
|
||||
},
|
||||
'setting.D': {
|
||||
@@ -67,8 +65,7 @@ suite('AccountPolicyService', () => {
|
||||
policy: {
|
||||
name: 'PolicySettingD',
|
||||
minimumVersion: '1.0.0',
|
||||
defaultValue: false,
|
||||
tags: [PolicyTag.Account, PolicyTag.Preview]
|
||||
value: account => account.chat_preview_features_enabled === false ? false : undefined,
|
||||
}
|
||||
},
|
||||
'setting.E': {
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import assert from 'assert';
|
||||
import { PolicyTag } from '../../../../../base/common/policy.js';
|
||||
import { NullLogService } from '../../../../../platform/log/common/log.js';
|
||||
import { DefaultAccountService, IDefaultAccount, IDefaultAccountService } from '../../../accounts/common/defaultAccount.js';
|
||||
import { DefaultAccountService, IDefaultAccountService } from '../../../accounts/common/defaultAccount.js';
|
||||
import { AccountPolicyService } from '../../common/accountPolicyService.js';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
|
||||
import { Registry } from '../../../../../platform/registry/common/platform.js';
|
||||
@@ -19,6 +18,7 @@ import { IFileService } from '../../../../../platform/files/common/files.js';
|
||||
import { InMemoryFileSystemProvider } from '../../../../../platform/files/common/inMemoryFilesystemProvider.js';
|
||||
import { FileService } from '../../../../../platform/files/common/fileService.js';
|
||||
import { VSBuffer } from '../../../../../base/common/buffer.js';
|
||||
import { IDefaultAccount } from '../../../../../base/common/defaultAccount.js';
|
||||
|
||||
const BASE_DEFAULT_ACCOUNT: IDefaultAccount = {
|
||||
enterprise: false,
|
||||
@@ -56,8 +56,7 @@ suite('MultiplexPolicyService', () => {
|
||||
policy: {
|
||||
name: 'PolicySettingB',
|
||||
minimumVersion: '1.0.0',
|
||||
defaultValue: "policyValueB",
|
||||
tags: [PolicyTag.Account, PolicyTag.Preview]
|
||||
value: account => account.chat_preview_features_enabled === false ? 'policyValueB' : undefined,
|
||||
}
|
||||
},
|
||||
'setting.C': {
|
||||
@@ -66,8 +65,7 @@ suite('MultiplexPolicyService', () => {
|
||||
policy: {
|
||||
name: 'PolicySettingC',
|
||||
minimumVersion: '1.0.0',
|
||||
defaultValue: JSON.stringify(['policyValueC1', 'policyValueC2']),
|
||||
tags: [PolicyTag.Account, PolicyTag.Preview]
|
||||
value: account => account.chat_preview_features_enabled === false ? JSON.stringify(['policyValueC1', 'policyValueC2']) : undefined,
|
||||
}
|
||||
},
|
||||
'setting.D': {
|
||||
@@ -76,8 +74,7 @@ suite('MultiplexPolicyService', () => {
|
||||
policy: {
|
||||
name: 'PolicySettingD',
|
||||
minimumVersion: '1.0.0',
|
||||
defaultValue: false,
|
||||
tags: [PolicyTag.Account, PolicyTag.Preview]
|
||||
value: account => account.chat_preview_features_enabled === false ? false : undefined,
|
||||
}
|
||||
},
|
||||
'setting.E': {
|
||||
|
||||
Reference in New Issue
Block a user