Skip to content

Commit 0c47509

Browse files
feat: Add ability to manage extensions installation using a ConfigMap
Signed-off-by: Roman Nikitenko <rnikiten@redhat.com> Assisted-by: Cursor AI
1 parent c544f0f commit 0c47509

6 files changed

Lines changed: 99 additions & 4 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**********************************************************************
2+
* Copyright (c) 2025 Red Hat, Inc.
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
***********************************************************************/
10+
/* eslint-disable header/header */
11+
12+
import * as fs from 'fs';
13+
import { URI } from '../../../base/common/uri.js';
14+
import { DisposableStore } from '../../../base/common/lifecycle.js';
15+
import { FileService } from '../../../platform/files/common/fileService.js';
16+
import { LogService } from '../../../platform/log/common/logService.js';
17+
import { FilePolicyService } from '../../../platform/policy/common/filePolicyService.js';
18+
import { IPolicyService, NullPolicyService } from '../../../platform/policy/common/policy.js';
19+
import { ServerEnvironmentService } from '../serverEnvironmentService.js';
20+
21+
const POLICY_FILE_PATH = '/checode-config/policy.json';
22+
23+
export function getPolicyFile(): URI | undefined {
24+
if (fs.existsSync(POLICY_FILE_PATH)) {
25+
return URI.file(POLICY_FILE_PATH);
26+
}
27+
return undefined;
28+
}
29+
30+
export function getPolicyService(environmentService: ServerEnvironmentService, fileService: FileService, logService: LogService, disposables: DisposableStore): IPolicyService {
31+
if (environmentService.policyFile) {
32+
logService.info(`Using policy file: ${environmentService.policyFile.fsPath}`);
33+
return disposables.add(new FilePolicyService(environmentService.policyFile, fileService, logService));
34+
} else {
35+
logService.info('Policy file not found');
36+
return new NullPolicyService();
37+
}
38+
}

code/src/vs/server/node/serverEnvironmentService.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { memoize } from '../../base/common/decorators.js';
1313
import { URI } from '../../base/common/uri.js';
1414
import { joinPath } from '../../base/common/resources.js';
1515
import { join } from '../../base/common/path.js';
16+
import { getPolicyFile } from './che/serverServices.js';
1617

1718
export const serverOptions: OptionDescriptions<Required<ServerParsedArgs>> = {
1819

@@ -240,4 +241,8 @@ export class ServerEnvironmentService extends NativeEnvironmentService implement
240241
@memoize
241242
get mcpResource(): URI { return joinPath(URI.file(join(this.userDataPath, 'User')), 'mcp.json'); }
242243
override get args(): ServerParsedArgs { return super.args as ServerParsedArgs; }
244+
@memoize
245+
override get policyFile(): URI | undefined {
246+
return getPolicyFile() || super.policyFile;
247+
}
243248
}

code/src/vs/server/node/serverServices.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ import { IExtensionsScannerService } from '../../platform/extensionManagement/co
6565
import { ExtensionsScannerService } from './extensionsScannerService.js';
6666
import { IExtensionsProfileScannerService } from '../../platform/extensionManagement/common/extensionsProfileScannerService.js';
6767
import { IUserDataProfilesService } from '../../platform/userDataProfile/common/userDataProfile.js';
68-
import { NullPolicyService } from '../../platform/policy/common/policy.js';
68+
import { PolicyChannel } from '../../platform/policy/common/policyIpc.js';
6969
import { OneDataSystemAppender } from '../../platform/telemetry/node/1dsAppender.js';
7070
import { LoggerService } from '../../platform/log/node/loggerService.js';
7171
import { ServerUserDataProfilesService } from '../../platform/userDataProfile/node/userDataProfile.js';
@@ -93,6 +93,7 @@ import { McpManagementChannel } from '../../platform/mcp/common/mcpManagementIpc
9393
import { AllowedMcpServersService } from '../../platform/mcp/common/allowedMcpServersService.js';
9494
import { IMcpGalleryManifestService } from '../../platform/mcp/common/mcpGalleryManifest.js';
9595
import { McpGalleryManifestIPCService } from '../../platform/mcp/common/mcpGalleryManifestServiceIpc.js';
96+
import { getPolicyService } from './che/serverServices.js';
9697

9798
const eventPrefix = 'monacoworkbench';
9899

@@ -139,7 +140,8 @@ export async function setupServerServices(connectionToken: ServerConnectionToken
139140
services.set(IUriIdentityService, uriIdentityService);
140141

141142
// Configuration
142-
const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService, new NullPolicyService(), logService);
143+
const policyService = getPolicyService(environmentService, fileService, logService, disposables);
144+
const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService, policyService, logService);
143145
services.set(IConfigurationService, configurationService);
144146

145147
// User Data Profiles
@@ -257,6 +259,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken
257259
socketServer.registerChannel('extensions', channel);
258260

259261
socketServer.registerChannel('mcpManagement', new McpManagementChannel(mcpManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority)));
262+
socketServer.registerChannel('policy', new PolicyChannel(policyService));
260263

261264
// clean up extensions folder
262265
remoteExtensionsScanner.whenExtensionsReady().then(() => extensionManagementService.cleanUp());
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**********************************************************************
2+
* Copyright (c) 2025 Red Hat, Inc.
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
***********************************************************************/
10+
/* eslint-disable header/header */
11+
12+
import { ILogService } from '../../../platform/log/common/log.js';
13+
import { IPolicyService, NullPolicyService } from '../../../platform/policy/common/policy.js';
14+
import { PolicyChannelClient } from '../../../platform/policy/common/policyIpc.js';
15+
import { IRemoteAgentService } from '../../services/remote/common/remoteAgentService.js';
16+
17+
// Get policy service from remote agent if available, otherwise use NullPolicyService
18+
export function getPolicyService(remoteAgentService: IRemoteAgentService, logService: ILogService, remoteAuthority?: string): IPolicyService {
19+
20+
let policyService: IPolicyService;
21+
if (remoteAuthority) {
22+
try {
23+
const connection = remoteAgentService.getConnection();
24+
if (connection) {
25+
const policyChannel = connection.getChannel('policy');
26+
// PolicyChannelClient needs initial policiesData - start with empty object, policies will be loaded when definitions are registered
27+
policyService = new PolicyChannelClient({}, policyChannel);
28+
logService.info('Policy channel client was created successfully');
29+
} else {
30+
logService.warn('Failed to get remote aget connection, using NullPolicyService');
31+
policyService = new NullPolicyService();
32+
}
33+
} catch (error) {
34+
console.log('/// web main /// connection - ERROR ');
35+
logService.warn('Failed to create policy channel client, using NullPolicyService', error);
36+
policyService = new NullPolicyService();
37+
}
38+
} else {
39+
logService.warn('Failed to create policy channel client(no remote authority), using NullPolicyService');
40+
policyService = new NullPolicyService();
41+
}
42+
return policyService;
43+
}

code/src/vs/workbench/browser/web.main.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ import { IProgressService } from '../../platform/progress/common/progress.js';
6868
import { DelayedLogChannel } from '../services/output/common/delayedLogChannel.js';
6969
import { dirname, joinPath } from '../../base/common/resources.js';
7070
import { IUserDataProfile, IUserDataProfilesService } from '../../platform/userDataProfile/common/userDataProfile.js';
71-
import { NullPolicyService } from '../../platform/policy/common/policy.js';
7271
import { IRemoteExplorerService } from '../services/remote/common/remoteExplorerService.js';
7372
import { DisposableTunnel, TunnelProtocol } from '../../platform/tunnel/common/tunnel.js';
7473
import { ILabelService } from '../../platform/label/common/label.js';
@@ -95,6 +94,7 @@ import { ISecretStorageService } from '../../platform/secrets/common/secrets.js'
9594
import { TunnelSource } from '../services/remote/common/tunnelModel.js';
9695
import { mainWindow } from '../../base/browser/window.js';
9796
import { INotificationService, Severity } from '../../platform/notification/common/notification.js';
97+
import { getPolicyService } from './che/web.js';
9898

9999
export class BrowserMain extends Disposable {
100100

@@ -567,7 +567,7 @@ export class BrowserMain extends Disposable {
567567
}
568568

569569
const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData, Schemas.tmp] /* Cache all non native resources */, environmentService, fileService);
570-
const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, new NullPolicyService());
570+
const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, getPolicyService(remoteAgentService,logService, this.configuration.remoteAuthority));
571571

572572
try {
573573
await workspaceService.initialize(workspace);

code/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2406,6 +2406,12 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
24062406
}
24072407

24082408
if (extension.gallery) {
2409+
// Check if extension is allowed first (before checking platform compatibility or signing)
2410+
const allowedResult = this.allowedExtensionsService.isAllowed({ id: extension.gallery.identifier.id, publisherDisplayName: extension.gallery.publisherDisplayName });
2411+
if (allowedResult !== true) {
2412+
return new MarkdownString(nls.localize('extension not allowed to install', "This extension cannot be installed because it is not in the allowed list."));
2413+
}
2414+
24092415
if (!extension.gallery.isSigned && shouldRequireRepositorySignatureFor(extension.private, await this.extensionGalleryManifestService.getExtensionGalleryManifest())) {
24102416
return new MarkdownString().appendText(nls.localize('not signed', "This extension is not signed."));
24112417
}

0 commit comments

Comments
 (0)