diff --git a/extensions/agent/src/data/proxyData.ts b/extensions/agent/src/data/proxyData.ts index d05683db73..7d7352db2a 100644 --- a/extensions/agent/src/data/proxyData.ts +++ b/extensions/agent/src/data/proxyData.ts @@ -35,6 +35,7 @@ export class ProxyData implements IAgentDialogData { public async save() { let agentService = await AgentUtils.getAgentService(); let result = await agentService.createProxy(this.ownerUri, this.toAgentProxyInfo()); + console.log(result); if (!result || !result.success) { // TODO handle error here } diff --git a/extensions/agent/src/dialogs/proxyDialog.ts b/extensions/agent/src/dialogs/proxyDialog.ts index 84d446af3f..453c698160 100644 --- a/extensions/agent/src/dialogs/proxyDialog.ts +++ b/extensions/agent/src/dialogs/proxyDialog.ts @@ -15,36 +15,60 @@ const localize = nls.loadMessageBundle(); export class ProxyDialog extends AgentDialog { // Top level - private static readonly CreateDialogTitle: string = localize('createProxy.createAlert', 'Create Alert'); - private static readonly EditDialogTitle: string = localize('createProxy.createAlert', 'Create Alert'); + private static readonly CreateDialogTitle: string = localize('createProxy.createProxy', 'Create Proxy'); + private static readonly EditDialogTitle: string = localize('createProxy.editProxy', 'Edit Proxy'); private static readonly GeneralTabText: string = localize('createProxy.General', 'General'); // General tab strings private static readonly ProxyNameTextBoxLabel: string = localize('createProxy.ProxyName', 'Proxy name'); private static readonly CredentialNameTextBoxLabel: string = localize('createProxy.CredentialName', 'Credential name'); private static readonly DescriptionTextBoxLabel: string = localize('createProxy.Description', 'Description'); - private static readonly SubsystemsTableLabel: string = localize('createProxy.Subsystems', 'Subsystems'); - private static readonly SubsystemNameColumnLabel: string = localize('createProxy.SubsystemName', 'Subsystem'); + private static readonly SubsystemLabel: string = localize('createProxy.SubsystemName', 'Subsystem'); + private static readonly OperatingSystemLabel: string = localize('createProxy.OperatingSystem', 'Operating system (CmdExec)'); + private static readonly ReplicationSnapshotLabel: string = localize('createProxy.ReplicationSnapshot', 'Replication Snapshot'); + private static readonly ReplicationTransactionLogLabel: string = localize('createProxy.ReplicationTransactionLog', 'Replication Transaction-Log Reader'); + private static readonly ReplicationDistributorLabel: string = localize('createProxy.ReplicationDistributor', 'Replication Distributor'); + private static readonly ReplicationMergeLabel: string = localize('createProxy.ReplicationMerge', 'Replication Merge'); + private static readonly ReplicationQueueReaderLabel: string = localize('createProxy.ReplicationQueueReader', 'Replication Queue Reader'); + private static readonly SSASQueryLabel: string = localize('createProxy.SSASQueryLabel', 'SQL Server Analysis Services Query'); + private static readonly SSASCommandLabel: string = localize('createProxy.SSASCommandLabel', 'SQL Server Analysis Services Command'); + private static readonly SSISPackageLabel: string = localize('createProxy.SSISPackage', 'SQL Server Integration Services Package'); + private static readonly PowerShellLabel: string = localize('createProxy.PowerShell', 'PowerShell'); + private static readonly SubSystemHeadingLabel: string = localize('createProxy.subSystemHeading', 'Active to the following subsytems'); // UI Components private generalTab: sqlops.window.modelviewdialog.DialogTab; // General tab controls private proxyNameTextBox: sqlops.InputBoxComponent; - private credentialNameTextBox: sqlops.InputBoxComponent; + private credentialNameDropDown: sqlops.DropDownComponent; private descriptionTextBox: sqlops.InputBoxComponent; - private subsystemsTable: sqlops.TableComponent; + private subsystemCheckBox: sqlops.CheckBoxComponent; + private operatingSystemCheckBox: sqlops.CheckBoxComponent; + private replicationSnapshotCheckBox: sqlops.CheckBoxComponent; + private replicationTransactionLogCheckBox: sqlops.CheckBoxComponent; + private replicationDistributorCheckBox: sqlops.CheckBoxComponent; + private replicationMergeCheckbox: sqlops.CheckBoxComponent; + private replicationQueueReaderCheckbox: sqlops.CheckBoxComponent; + private sqlQueryCheckBox: sqlops.CheckBoxComponent; + private sqlCommandCheckBox: sqlops.CheckBoxComponent; + private sqlIntegrationServicesPackageCheckbox: sqlops.CheckBoxComponent; + private powershellCheckBox: sqlops.CheckBoxComponent; - constructor(ownerUri: string, proxyInfo: sqlops.AgentProxyInfo = undefined) { + private credentials: sqlops.CredentialInfo[]; + + constructor(ownerUri: string, proxyInfo: sqlops.AgentProxyInfo = undefined, credentials: sqlops.CredentialInfo[]) { super( ownerUri, new ProxyData(ownerUri, proxyInfo), proxyInfo ? ProxyDialog.EditDialogTitle : ProxyDialog.CreateDialogTitle); + this.credentials = credentials; } protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) { this.generalTab = sqlops.window.modelviewdialog.createTab(ProxyDialog.GeneralTabText); + this.initializeGeneralTab(); this.dialog.content = [this.generalTab]; @@ -53,47 +77,143 @@ export class ProxyDialog extends AgentDialog { private initializeGeneralTab() { this.generalTab.registerContent(async view => { - this.proxyNameTextBox = view.modelBuilder.inputBox().component(); + this.proxyNameTextBox = view.modelBuilder.inputBox() + .withProperties({width: 420}) + .component(); - this.credentialNameTextBox = view.modelBuilder.inputBox().component(); - - this.descriptionTextBox = view.modelBuilder.inputBox().component(); - - this.subsystemsTable = view.modelBuilder.table() + this.credentialNameDropDown = view.modelBuilder.dropDown() .withProperties({ - columns: [ - ProxyDialog.SubsystemNameColumnLabel - ], - data: [], - height: 500 + width: 432, + value: '', + editable: true, + values: this.credentials.length > 0 ? this.credentials.map(c => c.name) : [''] + }) + .component(); + + this.descriptionTextBox = view.modelBuilder.inputBox() + .withProperties({ + width: 420, + multiline: true, + height: 300 + }) + .component(); + + this.subsystemCheckBox = view.modelBuilder.checkBox() + .withProperties({ + label: ProxyDialog.SubsystemLabel }).component(); + this.subsystemCheckBox.onChanged(() => { + if (this.subsystemCheckBox.checked) { + this.operatingSystemCheckBox.checked = true; + this.replicationSnapshotCheckBox.checked = true; + this.replicationTransactionLogCheckBox.checked = true; + this.replicationDistributorCheckBox.checked = true; + this.replicationMergeCheckbox.checked = true; + this.replicationQueueReaderCheckbox.checked = true; + this.sqlQueryCheckBox.checked = true; + this.sqlCommandCheckBox.checked = true; + this.sqlIntegrationServicesPackageCheckbox.checked = true; + this.powershellCheckBox.checked = true; + } else { + this.operatingSystemCheckBox.checked = false; + this.replicationSnapshotCheckBox.checked = false; + this.replicationTransactionLogCheckBox.checked = false; + this.replicationDistributorCheckBox.checked = false; + this.replicationMergeCheckbox.checked = false; + this.replicationQueueReaderCheckbox.checked = false; + this.sqlQueryCheckBox.checked = false; + this.sqlCommandCheckBox.checked = false; + this.sqlIntegrationServicesPackageCheckbox.checked = false; + this.powershellCheckBox.checked = false; + } + }); + + this.operatingSystemCheckBox = view.modelBuilder.checkBox() + .withProperties({ + label: ProxyDialog.OperatingSystemLabel + }).component(); + + this.replicationSnapshotCheckBox = view.modelBuilder.checkBox() + .withProperties({ + label: ProxyDialog.ReplicationSnapshotLabel + }).component(); + + this.replicationTransactionLogCheckBox = view.modelBuilder.checkBox() + .withProperties({ + label: ProxyDialog.ReplicationTransactionLogLabel + }).component(); + + this.replicationDistributorCheckBox = view.modelBuilder.checkBox() + .withProperties({ + label: ProxyDialog.ReplicationDistributorLabel + }).component(); + + this.replicationMergeCheckbox = view.modelBuilder.checkBox() + .withProperties({ + label: ProxyDialog.ReplicationMergeLabel + }).component(); + + this.replicationQueueReaderCheckbox = view.modelBuilder.checkBox() + .withProperties({ + label: ProxyDialog.ReplicationQueueReaderLabel + }).component(); + + this.sqlQueryCheckBox = view.modelBuilder.checkBox() + .withProperties({ + label: ProxyDialog.SSASQueryLabel + }).component(); + + this.sqlCommandCheckBox = view.modelBuilder.checkBox() + .withProperties({ + label: ProxyDialog.SSASCommandLabel + }).component(); + + this.sqlIntegrationServicesPackageCheckbox = view.modelBuilder.checkBox() + .withProperties({ + label: ProxyDialog.SSISPackageLabel + }).component(); + + this.powershellCheckBox = view.modelBuilder.checkBox() + .withProperties({ + label: ProxyDialog.PowerShellLabel + }).component(); + + let checkBoxContainer = view.modelBuilder.groupContainer() + .withItems([this.operatingSystemCheckBox, this.replicationSnapshotCheckBox, + this.replicationTransactionLogCheckBox, this.replicationDistributorCheckBox, this.replicationMergeCheckbox, + this.replicationQueueReaderCheckbox, this.sqlQueryCheckBox, this.sqlCommandCheckBox, this.sqlIntegrationServicesPackageCheckbox, + this.powershellCheckBox]) + .component(); + let formModel = view.modelBuilder.formContainer() .withFormItems([{ component: this.proxyNameTextBox, title: ProxyDialog.ProxyNameTextBoxLabel }, { - component: this.credentialNameTextBox, + component: this.credentialNameDropDown, title: ProxyDialog.CredentialNameTextBoxLabel }, { component: this.descriptionTextBox, title: ProxyDialog.DescriptionTextBoxLabel - }, { - component: this.subsystemsTable, - title: ProxyDialog.SubsystemsTableLabel - }]).withLayout({ width: '100%' }).component(); + }]).withLayout({ width: 420 }).component(); await view.initializeModel(formModel); this.proxyNameTextBox.value = this.model.accountName; - this.credentialNameTextBox.value = this.model.credentialName; + this.credentialNameDropDown.value = this.model.credentialName; this.descriptionTextBox.value = this.model.description; }); } + protected updateModel() { this.model.accountName = this.proxyNameTextBox.value; - this.model.credentialName = this.credentialNameTextBox.value; + this.model.credentialName = this.credentialNameDropDown.value as string; + this.model.credentialId = this.credentials.find( + c => c.name === this.model.credentialName).id; + this.model.credentialIdentity = this.credentials.find( + c => c.name === this.model.credentialName).identity; this.model.description = this.descriptionTextBox.value; } } diff --git a/extensions/agent/src/mainController.ts b/extensions/agent/src/mainController.ts index 8216fed1fb..30b446fd59 100644 --- a/extensions/agent/src/mainController.ts +++ b/extensions/agent/src/mainController.ts @@ -47,8 +47,8 @@ export class MainController { let dialog = new OperatorDialog(ownerUri, operatorInfo); dialog.openDialog(); }); - vscode.commands.registerCommand('agent.openProxyDialog', (ownerUri: string, proxyInfo: sqlops.AgentProxyInfo) => { - let dialog = new ProxyDialog(ownerUri, proxyInfo); + vscode.commands.registerCommand('agent.openProxyDialog', (ownerUri: string, proxyInfo: sqlops.AgentProxyInfo, credentials: sqlops.CredentialInfo[]) => { + let dialog = new ProxyDialog(ownerUri, proxyInfo, credentials); dialog.openDialog(); }); } diff --git a/extensions/agent/src/test/testAgentService.ts b/extensions/agent/src/test/testAgentService.ts index 31d6b9e1a0..914f74e54b 100644 --- a/extensions/agent/src/test/testAgentService.ts +++ b/extensions/agent/src/test/testAgentService.ts @@ -86,6 +86,11 @@ export class TestAgentService implements sqlops.AgentServicesProvider { return undefined; } + // Agent Credential method + getCredentials(ownerUri: string): Thenable { + return undefined; + } + // Job Schedule management methods getJobSchedules(ownerUri: string): Thenable { return undefined; diff --git a/extensions/mssql/src/contracts.ts b/extensions/mssql/src/contracts.ts index 1fd0fa1a2a..eab6339bc3 100644 --- a/extensions/mssql/src/contracts.ts +++ b/extensions/mssql/src/contracts.ts @@ -148,6 +148,11 @@ export interface DeleteAgentProxyParams { proxy: sqlops.AgentProxyInfo; } +// Agent Credentials parameters +export interface GetCredentialsParams { + ownerUri: string; +} + // Job Schedule management parameters export interface AgentJobScheduleParams { ownerUri: string; @@ -262,6 +267,11 @@ export namespace DeleteAgentProxyRequest { export const type = new RequestType('agent/deleteproxy'); } +// Agent Credentials request +export namespace AgentCredentialsRequest { + export const type = new RequestType('security/credentials'); +} + // Job Schedules requests export namespace AgentJobSchedulesRequest { export const type = new RequestType('agent/schedules'); diff --git a/extensions/mssql/src/features.ts b/extensions/mssql/src/features.ts index 71ac7a9630..57db9f1f33 100644 --- a/extensions/mssql/src/features.ts +++ b/extensions/mssql/src/features.ts @@ -438,6 +438,22 @@ export class AgentServicesFeature extends SqlOpsFeature { ); }; + // Agent Credential Method + let getCredentials = (ownerUri: string): Thenable => { + let params: contracts.GetCredentialsParams = { + ownerUri: ownerUri + }; + let requestType = contracts.AgentCredentialsRequest.type; + return client.sendRequest(requestType, params).then( + r => r, + e => { + client.logFailedRequest(requestType, e); + return Promise.resolve(undefined); + } + ); + }; + + // Job Schedule management methods let getJobSchedules = (ownerUri: string): Thenable => { let params: contracts.AgentJobScheduleParams = { @@ -532,6 +548,7 @@ export class AgentServicesFeature extends SqlOpsFeature { createProxy, updateProxy, deleteProxy, + getCredentials, getJobSchedules, createJobSchedule, updateJobSchedule, diff --git a/src/sql/parts/jobManagement/common/interfaces.ts b/src/sql/parts/jobManagement/common/interfaces.ts index e157ee90e8..f347da987e 100644 --- a/src/sql/parts/jobManagement/common/interfaces.ts +++ b/src/sql/parts/jobManagement/common/interfaces.ts @@ -34,6 +34,8 @@ export interface IJobManagementService { getProxies(connectionUri: string): Thenable; deleteProxy(connectionUri: string, proxy: sqlops.AgentProxyInfo): Thenable; + getCredentials(connectionUri: string): Thenable; + jobAction(connectionUri: string, jobName: string, action: string): Thenable; addToCache(server: string, cache: JobCacheObject); jobCacheObjectMap: { [server: string]: JobCacheObject; }; diff --git a/src/sql/parts/jobManagement/common/jobManagementService.ts b/src/sql/parts/jobManagement/common/jobManagementService.ts index fb3c4b8940..401c487a5f 100644 --- a/src/sql/parts/jobManagement/common/jobManagementService.ts +++ b/src/sql/parts/jobManagement/common/jobManagementService.ts @@ -78,6 +78,12 @@ export class JobManagementService implements IJobManagementService { }); } + public getCredentials(connectionUri: string): Thenable { + return this._runAction(connectionUri, (runner) => { + return runner.getCredentials(connectionUri); + }); + } + public getJobHistory(connectionUri: string, jobID: string): Thenable { return this._runAction(connectionUri, (runner) => { return runner.getJobHistory(connectionUri, jobID); diff --git a/src/sql/parts/jobManagement/views/proxiesView.component.ts b/src/sql/parts/jobManagement/views/proxiesView.component.ts index b009a9298a..d9f0e14667 100644 --- a/src/sql/parts/jobManagement/views/proxiesView.component.ts +++ b/src/sql/parts/jobManagement/views/proxiesView.component.ts @@ -166,7 +166,11 @@ export class ProxiesViewComponent extends JobManagementView implements OnInit { public openCreateProxyDialog() { let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri; - this._commandService.executeCommand('agent.openProxyDialog', ownerUri); + this._jobManagementService.getCredentials(ownerUri).then((result) => { + if (result && result.credentials) { + this._commandService.executeCommand('agent.openProxyDialog', ownerUri, undefined, result.credentials); + } + }); } private refreshJobs() { diff --git a/src/sql/sqlops.d.ts b/src/sql/sqlops.d.ts index 1cc9ddd1a3..83b019c959 100644 --- a/src/sql/sqlops.d.ts +++ b/src/sql/sqlops.d.ts @@ -1503,6 +1503,9 @@ declare module 'sqlops' { updateProxy(ownerUri: string, originalProxyName: string, proxyInfo: AgentProxyInfo): Thenable; deleteProxy(ownerUri: string, proxyInfo: AgentProxyInfo): Thenable; + // Credential method + getCredentials(ownerUri: string): Thenable; + // Job Schedule management methods getJobSchedules(ownerUri: string): Thenable; createJobSchedule(ownerUri: string, scheduleInfo: AgentJobScheduleInfo): Thenable; @@ -1512,6 +1515,20 @@ declare module 'sqlops' { registerOnUpdated(handler: () => any): void; } + // Security service interfaces ------------------------------------------------------------------------ + export interface CredentialInfo { + id: number; + identity: string; + name: string; + dateLastModified: string; + createDate: string; + providerName: string; + } + + export interface GetCredentialsResult extends ResultStatus { + credentials: CredentialInfo[]; + } + // Task service interfaces ---------------------------------------------------------------------------- export enum TaskStatus { notStarted = 0, diff --git a/src/sql/workbench/api/node/extHostDataProtocol.ts b/src/sql/workbench/api/node/extHostDataProtocol.ts index 7735a38ffa..30b6898c6a 100644 --- a/src/sql/workbench/api/node/extHostDataProtocol.ts +++ b/src/sql/workbench/api/node/extHostDataProtocol.ts @@ -604,6 +604,13 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { return this._resolveProvider(handle).deleteProxy(ownerUri, proxy); } + /** + * Gets Agent Credentials from server + */ + $getCredentials(handle: number, ownerUri: string): Thenable { + return this._resolveProvider(handle).getCredentials(ownerUri); + } + /** * SQL Agent job data update notification */ diff --git a/src/sql/workbench/api/node/mainThreadDataProtocol.ts b/src/sql/workbench/api/node/mainThreadDataProtocol.ts index 92776e0c95..047e552d6a 100644 --- a/src/sql/workbench/api/node/mainThreadDataProtocol.ts +++ b/src/sql/workbench/api/node/mainThreadDataProtocol.ts @@ -366,6 +366,9 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { deleteProxy(connectionUri: string, proxyInfo: sqlops.AgentProxyInfo): Thenable { return self._proxy.$deleteProxy(handle, connectionUri, proxyInfo); }, + getCredentials(connectionUri: string): Thenable { + return self._proxy.$getCredentials(handle, connectionUri); + } }); return undefined; diff --git a/src/sql/workbench/api/node/sqlExtHost.protocol.ts b/src/sql/workbench/api/node/sqlExtHost.protocol.ts index a37d267a00..e357da8e37 100644 --- a/src/sql/workbench/api/node/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/node/sqlExtHost.protocol.ts @@ -372,6 +372,11 @@ export abstract class ExtHostDataProtocolShape { * Deletes a proxy */ $deleteProxy(handle: number, connectionUri: string, proxy: sqlops.AgentProxyInfo): Thenable { throw ni(); } + + /** + * Get Agent Credentials list + */ + $getCredentials(handle: number, connectionUri: string): Thenable { throw ni(); } } /**