[SQL Migration] Properly respect user's encryptConnection and trustServerCertificate settings (#21824)

* WIP

* Always get latest current connection

* Update more references

* Clean up

* Clean up

* vbump

* Update comments

* Address PR feedback

* Separate into helper methods
This commit is contained in:
Raymond Truong
2023-02-08 10:12:11 -08:00
committed by GitHub
parent 99a924dbcd
commit 480d8e2cd0
25 changed files with 206 additions and 154 deletions

View File

@@ -14,7 +14,7 @@ import { v4 as uuidv4 } from 'uuid';
import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews, logError } from '../telemetry';
import { hashString, deepClone } from '../api/utils';
import { SKURecommendationPage } from '../wizard/skuRecommendationPage';
import { excludeDatabases, getConnectionProfile, LoginTableInfo, SourceDatabaseInfo, TargetDatabaseInfo } from '../api/sqlUtils';
import { excludeDatabases, getEncryptConnectionValue, getSourceConnectionId, getSourceConnectionProfile, getSourceConnectionServerInfo, getSourceConnectionString, getSourceConnectionUri, getTargetConnectionString, getTrustServerCertificateValue, LoginTableInfo, SourceDatabaseInfo, TargetDatabaseInfo } from '../api/sqlUtils';
import { LoginMigrationModel, LoginMigrationStep } from './loginMigrationModel';
import { TdeMigrationDbResult, TdeMigrationModel } from './tdeModels';
import { NetworkInterfaceModel } from '../api/dataModels/azure/networkInterfaceModel';
@@ -134,7 +134,6 @@ export interface Blob {
}
export interface Model {
readonly sourceConnectionId: string;
readonly currentState: State;
gatheringInformationError: string | undefined;
_azureAccount: azdata.Account | undefined;
@@ -297,7 +296,6 @@ export class MigrationStateModel implements Model, vscode.Disposable {
constructor(
public extensionContext: vscode.ExtensionContext,
private readonly _sourceConnectionId: string,
public readonly migrationService: mssql.ISqlMigrationService,
public readonly tdeMigrationService: mssql.ITdeMigrationService
) {
@@ -373,10 +371,6 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|| this.isBackupContainerNetworkShare;
}
public get sourceConnectionId(): string {
return this._sourceConnectionId;
}
public get currentState(): State {
return this._currentState;
}
@@ -387,7 +381,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
this._stateChangeEventEmitter.fire({ oldState, newState: this.currentState });
}
public async getDatabases(): Promise<string[]> {
const temp = await azdata.connection.listDatabases(this.sourceConnectionId);
const temp = await azdata.connection.listDatabases(await getSourceConnectionId());
const finalResult = temp.filter((name) => !excludeDatabases.includes(name));
return finalResult;
}
@@ -404,7 +398,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
}
public async getDatabaseAssessments(targetType: MigrationTargetType[]): Promise<ServerAssessment> {
const ownerUri = await azdata.connection.getUriForConnection(this.sourceConnectionId);
const ownerUri = await getSourceConnectionUri();
try {
const xEventsFilesFolderPath = ''; // to-do: collect by prompting the user in the UI - for now, blank = disabled
const response = (await this.migrationService.getAssessments(ownerUri, this._databasesForAssessment, xEventsFilesFolderPath))!;
@@ -472,8 +466,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
let fullInstanceName: string;
// execute a query against the source to get the correct instance name
const connectionProfile = await this.getSourceConnectionProfile();
const connectionUri = await azdata.connection.getUriForConnection(this._sourceConnectionId);
const connectionProfile = await getSourceConnectionProfile();
const connectionUri = await getSourceConnectionUri();
const queryProvider = azdata.dataprotocol.getProvider<azdata.QueryProvider>(connectionProfile.providerId, azdata.DataProviderType.QueryProvider);
const queryString = 'SELECT SERVERPROPERTY(\'ServerName\');';
const queryResult = await queryProvider.runQueryAndReturn(connectionUri, queryString);
@@ -482,9 +476,9 @@ export class MigrationStateModel implements Model, vscode.Disposable {
fullInstanceName = queryResult.rows[0][0].displayValue;
} else {
// get the instance name from connection info in case querying for the instance name doesn't work for whatever reason
const serverInfo = await azdata.connection.getServerInfo(this.sourceConnectionId);
const machineName = (<any>serverInfo)['machineName']; // contains the correct machine name but not necessarily the correct instance name
const instanceName = (await this.getSourceConnectionProfile()).serverName; // contains the correct instance name but not necessarily the correct machine name
const serverInfo = await getSourceConnectionServerInfo();
const machineName = (<any>serverInfo)['machineName']; // contains the correct machine name but not necessarily the correct instance name
const instanceName = connectionProfile.serverName; // contains the correct instance name but not necessarily the correct machine name
if (instanceName.includes('\\')) {
fullInstanceName = machineName + '\\' + instanceName.substring(instanceName.indexOf('\\') + 1);
@@ -554,26 +548,6 @@ export class MigrationStateModel implements Model, vscode.Disposable {
return this._targetServerName;
}
public async getSourceConnectionString(): Promise<string> {
return await azdata.connection.getConnectionString(this._sourceConnectionId, true);
}
public async getTargetConnectionString(): Promise<string> {
const connectionProfile = getConnectionProfile(
this.targetServerName,
this._targetServerInstance.id,
this._targetUserName,
this._targetPassword,
true /* trustServerCertificate */);
const result = await azdata.connection.connect(connectionProfile, false, false);
if (result.connected && result.connectionId) {
return azdata.connection.getConnectionString(result.connectionId, true);
}
return '';
}
private updateLoginMigrationResults(newResult: mssql.StartLoginMigrationResult): void {
if (this._loginMigrationsResult && this._loginMigrationsResult.exceptionMap) {
for (var key in newResult.exceptionMap) {
@@ -588,8 +562,17 @@ export class MigrationStateModel implements Model, vscode.Disposable {
try {
this._loginMigrationModel.AddNewLogins(this._loginsForMigration.map(row => row.loginName));
const sourceConnectionString = await this.getSourceConnectionString();
const targetConnectionString = await this.getTargetConnectionString();
const sourceConnectionString = await getSourceConnectionString();
const targetConnectionString = await getTargetConnectionString(
this.targetServerName,
this._targetServerInstance.id,
this._targetUserName,
this._targetPassword,
// for login migration, connect to target Azure SQL with true/true
// to-do: take as input from the user, should be true/false for DB/MI but true/true for VM
true /* encryptConnection */,
true /* trustServerCertificate */);
var response = (await this.migrationService.migrateLogins(
sourceConnectionString,
targetConnectionString,
@@ -612,8 +595,16 @@ export class MigrationStateModel implements Model, vscode.Disposable {
public async establishUserMappings(): Promise<Boolean> {
try {
const sourceConnectionString = await this.getSourceConnectionString();
const targetConnectionString = await this.getTargetConnectionString();
const sourceConnectionString = await getSourceConnectionString();
const targetConnectionString = await getTargetConnectionString(
this.targetServerName,
this._targetServerInstance.id,
this._targetUserName,
this._targetPassword,
// for login migration, connect to target Azure SQL with true/true
// to-do: take as input from the user, should be true/false for DB/MI but true/true for VM
true /* encryptConnection */,
true /* trustServerCertificate */);
var response = (await this.migrationService.establishUserMapping(
sourceConnectionString,
@@ -637,8 +628,16 @@ export class MigrationStateModel implements Model, vscode.Disposable {
public async migrateServerRolesAndSetPermissions(): Promise<Boolean> {
try {
const sourceConnectionString = await this.getSourceConnectionString();
const targetConnectionString = await this.getTargetConnectionString();
const sourceConnectionString = await getSourceConnectionString();
const targetConnectionString = await getTargetConnectionString(
this.targetServerName,
this._targetServerInstance.id,
this._targetUserName,
this._targetPassword,
// for login migration, connect to target Azure SQL with true/true
// to-do: take as input from the user, should be true/false for DB/MI but true/true for VM
true /* encryptConnection */,
true /* trustServerCertificate */);
var response = (await this.migrationService.migrateServerRolesAndSetPermissions(
sourceConnectionString,
@@ -782,7 +781,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
page: SKURecommendationPage): Promise<boolean> {
try {
if (!this.performanceCollectionInProgress()) {
const ownerUri = await azdata.connection.getUriForConnection(this.sourceConnectionId);
const ownerUri = await getSourceConnectionUri();
const response = await this.migrationService.startPerfDataCollection(
ownerUri,
dataFolder,
@@ -1080,12 +1079,6 @@ export class MigrationStateModel implements Model, vscode.Disposable {
return this.extensionContext.extensionPath;
}
public async getSourceConnectionProfile(): Promise<azdata.connection.ConnectionProfile> {
const sqlConnections = await azdata.connection.getConnections();
return sqlConnections.find(
value => value.connectionId === this.sourceConnectionId)!;
}
public getLocationDisplayName(location: string): Promise<string> {
return getLocationDisplayName(location);
}
@@ -1103,7 +1096,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
reportUpdate: (dbName: string, succeeded: boolean, message: string) => Promise<void>): Promise<OperationResult<TdeMigrationDbResult[]>> {
const tdeEnabledDatabases = this.tdeMigrationConfig.getTdeEnabledDatabases();
const connectionString = await azdata.connection.getConnectionString(this.sourceConnectionId, true);
const connectionString = await getSourceConnectionString();
const opResult: OperationResult<TdeMigrationDbResult[]> = {
success: false,
@@ -1148,10 +1141,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
}
public async startMigration() {
const sqlConnections = await azdata.connection.getConnections();
const currentConnection = sqlConnections.find(
value => value.connectionId === this.sourceConnectionId);
const currentConnection = await getSourceConnectionProfile();
const isOfflineMigration = this._databaseBackup.migrationMode === MigrationMode.OFFLINE;
const isSqlDbTarget = this.isSqlDbTarget;
@@ -1166,8 +1156,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
authentication: this._authenticationType,
userName: this._sqlServerUsername,
password: this._sqlServerPassword,
// to-do: use correct value of encryptConnection and trustServerCertificate
trustServerCertificate: currentConnection?.options.trustServerCertificate ?? false
encryptConnection: getEncryptConnectionValue(currentConnection),
trustServerCertificate: getTrustServerCertificateValue(currentConnection)
},
scope: this._targetServerInstance.id,
offlineConfiguration: {
@@ -1208,14 +1198,15 @@ export class MigrationStateModel implements Model, vscode.Disposable {
authentication: this._authenticationType,
userName: this._sqlServerUsername,
password: this._sqlServerPassword,
encryptConnection: true,
trustServerCertificate: currentConnection?.options.trustServerCertificate ?? false,
encryptConnection: getEncryptConnectionValue(currentConnection),
trustServerCertificate: getTrustServerCertificateValue(currentConnection)
};
requestBody.properties.targetSqlConnection = {
dataSource: sqlDbTarget.properties.fullyQualifiedDomainName,
authentication: MigrationSourceAuthenticationType.Sql,
userName: this._targetUserName,
password: this._targetPassword,
// when connecting to a target Azure SQL DB, use true/false
encryptConnection: true,
trustServerCertificate: false,
};