提交 5c53ddbb 编写于 作者: K kieferrm

model support

上级 96579057
......@@ -106,11 +106,14 @@ export interface IConfigModel<T> {
overrides: IOverrides<T>[];
keys: string[];
raw: any;
unfilteredRaw: any;
errors: any[];
merge(other: IConfigModel<T>, overwrite?: boolean): IConfigModel<T>;
config<V>(section: string): IConfigModel<V>;
configWithOverrides<V>(identifier: string, section?: string): IConfigModel<V>;
refilter(): void;
hasActiveFilter(): boolean;
}
export interface IOverrides<T> {
......
......@@ -17,6 +17,13 @@ export const Extensions = {
Configuration: 'base.contributions.configuration'
};
// Locally extend IJSONSchema with the vscode-specific `isExecutable` property
declare module 'vs/base/common/jsonSchema' {
export interface IJSONSchema {
isExecutable?: boolean;
}
}
export interface IConfigurationRegistry {
/**
......@@ -254,6 +261,32 @@ function getDefaultValue(type: string | string[]): any {
const configurationRegistry = new ConfigurationRegistry();
Registry.add(Extensions.Configuration, configurationRegistry);
export interface ISecurityConfiguration {
security: {
workspacesTrustedToSpecifyExecutables: { [path: string]: boolean }
};
}
configurationRegistry.registerConfiguration({
'id': 'Security',
'order': 5,
'title': nls.localize('securityConfigurationTitle', "Security"),
'type': 'object',
'properties': {
'security.workspacesTrustedToSpecifyExecutables': {
'type': 'object',
'description': nls.localize('security.workspacesTrustedToSpecifyExecutables', "Specifes which workspaces are trusted to specify executables in their settings. This option can only configured in the user settings."),
'default': {},
defaultSnippets: [{ body: '${1:workspace_path} : ${2:true}' }],
'additionalProperties': {
'type': 'boolean',
'description': nls.localize('exclude.boolean', "Path to a workspaces. Set to true or false to trust or distrust a workspace."),
}
}
}
});
const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IConfigurationNode>('configuration', [], {
description: nls.localize('vscode.extension.contributes.configuration', 'Contributes configuration settings.'),
type: 'object',
......@@ -267,9 +300,19 @@ const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IConfigu
description: nls.localize('vscode.extension.contributes.configuration.properties', 'Description of the configuration properties.'),
type: 'object',
additionalProperties: {
$ref: 'http://json-schema.org/draft-04/schema#'
anyOf: [
{ $ref: 'http://json-schema.org/draft-04/schema#' },
{
type: 'object',
properties: {
isExecutable: {
type: 'boolean'
}
}
}
]
}
}
},
}
});
......
......@@ -92,6 +92,7 @@ export class ConfigModel<T> implements IConfigModel<T> {
protected _overrides: IOverrides<T>[] = [];
private _raw: any = {};
private _unfilteredRaw: any = {};
private _parseErrors: any[] = [];
constructor(content: string, private name: string = '') {
......@@ -116,6 +117,10 @@ export class ConfigModel<T> implements IConfigModel<T> {
return this._raw;
}
public get unfilteredRaw(): T {
return this._unfilteredRaw;
}
public get errors(): any[] {
return this._parseErrors;
}
......@@ -222,7 +227,10 @@ export class ConfigModel<T> implements IConfigModel<T> {
this._raw = <T>{};
this._parseErrors = [e];
}
this._unfilteredRaw = this._raw;
this._raw = this.filterRaw(this._unfilteredRaw);
this._contents = toValuesTree(this._raw, message => console.error(`Conflict in settings file ${this.name}: ${message}`));
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
this._overrides = overrides.map<IOverrides<T>>(override => {
// Filter unknown and non-overridable properties
......@@ -238,6 +246,34 @@ export class ConfigModel<T> implements IConfigModel<T> {
};
});
}
/*
* If filterRaw is not a no-op, the returned object needs to be a copy.
* The input may not be modified in place. The default implementation
* is a no op.
*/
protected filterRaw(raw: any): any {
return raw;
}
public refilter(): void {
if (this._unfilteredRaw) {
this._raw = this.filterRaw(this._unfilteredRaw);
this._contents = toValuesTree(this._raw, message => console.error(`Conflict in settings file ${this.name}: ${message}`));
}
}
public hasActiveFilter(): boolean {
if (this._raw === this._unfilteredRaw) {
return false;
}
for (let key in this._unfilteredRaw) {
if (!this._raw.hasOwnProperty(key)) {
return true;
}
}
return false;
}
}
export class DefaultConfigModel<T> extends ConfigModel<T> {
......
......@@ -14,6 +14,18 @@ export const IWorkspaceConfigurationService = createDecorator<IWorkspaceConfigur
export type IWorkspaceConfigurationValues = { [key: string]: IWorkspaceConfigurationValue<any> };
export interface IWorkspaceTrust {
/**
* Returns iff the workspace is trusted by the user.
*/
isTrusted(): boolean;
/**
* Returns a hash of all known configuration keys that can be used to specify executables.
*/
allKnownConfigKeysForExecutables(): { [configKey: string]: any };
}
export interface IWorkspaceConfigurationService extends IConfigurationService {
/**
......@@ -21,6 +33,11 @@ export interface IWorkspaceConfigurationService extends IConfigurationService {
*/
hasWorkspaceConfiguration(): boolean;
/**
* Returns iff the workspace configuration contains configuration keys that are untrusted.
*/
hasUntrustedConfigurations(): boolean;
/**
* Override for the IConfigurationService#lookup() method that adds information about workspace settings.
*/
......
......@@ -6,7 +6,7 @@
import { ConfigModel } from 'vs/platform/configuration/common/model';
import { IConfigModel } from 'vs/platform/configuration/common/configuration';
import { WORKSPACE_STANDALONE_CONFIGURATIONS } from 'vs/workbench/services/configuration/common/configuration';
import { IWorkspaceTrust, WORKSPACE_STANDALONE_CONFIGURATIONS } from 'vs/workbench/services/configuration/common/configuration';
export class ScopedConfigModel<T> extends ConfigModel<T> {
......@@ -24,6 +24,32 @@ export class ScopedConfigModel<T> extends ConfigModel<T> {
}
export class TrustedWorkspaceSettingsConfigModel<T> extends ConfigModel<T> {
constructor(content: string, name: string = '', private workspaceTrust: IWorkspaceTrust = null) {
super(null, name);
if (content) {
this.update(content);
}
}
protected filterRaw(raw: any): { newRaw: any; removals: any } {
let allUntrustedKeys = {};
if (this.workspaceTrust && !this.workspaceTrust.isTrusted()) {
allUntrustedKeys = this.workspaceTrust.allKnownConfigKeysForExecutables();
}
let trustedProperties: any = {};
for (let property in raw) {
if (!allUntrustedKeys[property]) {
trustedProperties[property] = raw[property];
}
}
return trustedProperties;
}
}
export class WorkspaceConfigModel<T> extends ConfigModel<T> {
constructor(private workspaceSettingsConfig: IConfigModel<T>, private scopedConfigs: ScopedConfigModel<T>[]) {
......@@ -49,4 +75,23 @@ export class WorkspaceConfigModel<T> extends ConfigModel<T> {
});
return keys;
}
public refilter(): void {
this.workspaceSettingsConfig.refilter();
this.scopedConfigs.forEach(scopedConfigModel => {
scopedConfigModel.refilter();
});
}
public hasActiveFilter(): boolean {
if (this.workspaceSettingsConfig.hasActiveFilter()) {
return true;
}
this.scopedConfigs.forEach(scopedConfigModel => {
if (scopedConfigModel.hasActiveFilter()) {
return true;
}
});
return false;
}
}
\ No newline at end of file
......@@ -17,13 +17,16 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { readFile } from 'vs/base/node/pfs';
import errors = require('vs/base/common/errors');
import { ScopedConfigModel, WorkspaceConfigModel } from 'vs/workbench/services/configuration/common/model';
import { ScopedConfigModel, WorkspaceConfigModel, TrustedWorkspaceSettingsConfigModel } from 'vs/workbench/services/configuration/common/model';
import { IConfigurationServiceEvent, ConfigurationSource, getConfigurationValue, IConfigModel, IConfigurationOptions } from 'vs/platform/configuration/common/configuration';
import { ConfigModel } from 'vs/platform/configuration/common/model';
import { ConfigurationService as BaseConfigurationService } from 'vs/platform/configuration/node/configurationService';
import { IWorkspaceConfigurationValues, IWorkspaceConfigurationService, IWorkspaceConfigurationValue, CONFIG_DEFAULT_NAME, WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME, WORKSPACE_STANDALONE_CONFIGURATIONS, WORKSPACE_CONFIG_DEFAULT_PATH } from 'vs/workbench/services/configuration/common/configuration';
import { IWorkspaceConfigurationValues, IWorkspaceConfigurationService, IWorkspaceTrust, IWorkspaceConfigurationValue, CONFIG_DEFAULT_NAME, WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME, WORKSPACE_STANDALONE_CONFIGURATIONS, WORKSPACE_CONFIG_DEFAULT_PATH } from 'vs/workbench/services/configuration/common/configuration';
import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files';
import Event, { Emitter } from 'vs/base/common/event';
import { Registry } from 'vs/platform/platform';
import { IConfigurationRegistry, IConfigurationNode, Extensions, ISecurityConfiguration } from 'vs/platform/configuration/common/configurationRegistry';
interface IStat {
resource: uri;
......@@ -41,6 +44,39 @@ interface IWorkspaceConfiguration<T> {
consolidated: any;
}
export class WorkspaceTrust implements IWorkspaceTrust {
constructor(private contextService: IWorkspaceContextService, private baseConfigurationService: BaseConfigurationService<any>) { }
public isTrusted(): boolean {
let workspace = this.contextService.getWorkspace();
if (workspace) {
let path = workspace.resource.path;
let securityConfiguration = this.baseConfigurationService.getConfiguration<ISecurityConfiguration>();
let whiteList = securityConfiguration.security.workspacesTrustedToSpecifyExecutables;
return whiteList && whiteList[path];
}
return false;
}
public allKnownConfigKeysForExecutables(): { [key: string]: any } {
const configKeys: { [key: string]: boolean } = {};
const configurations: IConfigurationNode[] = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurations();
configurations.forEach((config) => {
const properties = config.properties;
if (properties) {
Object.keys(properties).map((key) => {
const property = properties[key];
if (property && property.isExecutable) {
configKeys[key] = true;
}
});
}
});
return configKeys;
}
}
/**
* Wraps around the basic configuration service and adds knowledge about workspace settings.
*/
......@@ -60,6 +96,8 @@ export class WorkspaceConfigurationService extends Disposable implements IWorksp
private workspaceFilePathToConfiguration: { [relativeWorkspacePath: string]: TPromise<IConfigModel<any>> };
private reloadConfigurationScheduler: RunOnceScheduler;
private workspaceTrust: IWorkspaceTrust;
constructor(
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IEnvironmentService environmentService: IEnvironmentService,
......@@ -84,26 +122,30 @@ export class WorkspaceConfigurationService extends Disposable implements IWorksp
.done(null, errors.onUnexpectedError), WorkspaceConfigurationService.RELOAD_CONFIGURATION_DELAY));
this._register(this.baseConfigurationService.onDidUpdateConfiguration(e => this.onBaseConfigurationChanged(e)));
this.workspaceTrust = new WorkspaceTrust(this.contextService, this.baseConfigurationService);
}
get onDidUpdateConfiguration(): Event<IConfigurationServiceEvent> {
return this._onDidUpdateConfiguration.event;
}
private onBaseConfigurationChanged(e: IConfigurationServiceEvent): void {
private onBaseConfigurationChanged(event: IConfigurationServiceEvent): void {
this.cachedWorkspaceConfig.refilter();
// update cached config when base config changes
const newConfig = new ConfigModel<any>(null)
const configModel = new ConfigModel<any>(null)
.merge(this.baseConfigurationService.getCache().consolidated) // global/default values (do NOT modify)
.merge(this.cachedWorkspaceConfig); // workspace configured values
// emit this as update to listeners if changed
if (!objects.equals(this.cachedConfig.contents, newConfig.contents)) {
this.cachedConfig = newConfig;
if (!objects.equals(this.cachedConfig.contents, configModel.contents)) {
this.cachedConfig = configModel;
this._onDidUpdateConfiguration.fire({
config: this.cachedConfig.contents,
source: e.source,
sourceConfig: e.sourceConfig
source: event.source,
sourceConfig: event.sourceConfig
});
}
}
......@@ -291,19 +333,23 @@ export class WorkspaceConfigurationService extends Disposable implements IWorksp
private createConfigModel<T>(content: IContent): IConfigModel<T> {
const path = this.contextService.toWorkspaceRelativePath(content.resource);
if (path === WORKSPACE_CONFIG_DEFAULT_PATH) {
return new ConfigModel<T>(content.value, content.resource.toString());
return new TrustedWorkspaceSettingsConfigModel<T>(content.value, content.resource.toString(), this.workspaceTrust);
} else {
const matches = /\/([^\.]*)*\.json/.exec(path);
if (matches && matches[1]) {
return new ScopedConfigModel<T>(content.value, content.resource.toString(), matches[1]);
}
}
return new ConfigModel<T>(null);
return new TrustedWorkspaceSettingsConfigModel<T>(null);
}
private isWorkspaceConfigurationFile(workspaceRelativePath: string): boolean {
return [WORKSPACE_CONFIG_DEFAULT_PATH, WORKSPACE_STANDALONE_CONFIGURATIONS.launch, WORKSPACE_STANDALONE_CONFIGURATIONS.tasks].some(p => p === workspaceRelativePath);
}
public hasUntrustedConfigurations(): boolean {
return this.cachedWorkspaceConfig.hasActiveFilter();
}
}
// node.hs helper functions
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册