mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-14 01:25:40 -05:00
Add change password function and handling (#1771)
* added ChangePassword to connectionService * added changepasswordparams * added more code * added more changes to connectionservice * added more changes * added small test * added changepasswordrequest * added different ServerConnection constructor * consolidated changepassword * added exception catch * added passwordChangeFail params * added changePassword to it's own function * simplified changePassword * made fixes to test * added new test * added one additional connection test * added response callback * removed unnecessary SendError * added localized empty password error * added updated error messages * added small fix to check * added changes based on feedback * added minor change * fix tests * renamed messages to errorDetails * simplified error message * small change to connectionservice message * error message change * added environment newline * added error retry messages to STS * added regex * added newline handling
This commit is contained in:
@@ -22,6 +22,7 @@ using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
{
|
||||
@@ -1060,6 +1061,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
// Register request and event handlers with the Service Host
|
||||
serviceHost.SetRequestHandler(ConnectionRequest.Type, HandleConnectRequest);
|
||||
serviceHost.SetRequestHandler(CancelConnectRequest.Type, HandleCancelConnectRequest);
|
||||
serviceHost.SetRequestHandler(ChangePasswordRequest.Type, HandleChangePasswordRequest);
|
||||
serviceHost.SetRequestHandler(DisconnectRequest.Type, HandleDisconnectRequest);
|
||||
serviceHost.SetRequestHandler(ListDatabasesRequest.Type, HandleListDatabasesRequest);
|
||||
serviceHost.SetRequestHandler(ChangeDatabaseRequest.Type, HandleChangeDatabaseRequest);
|
||||
@@ -1137,6 +1139,65 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
}).ContinueWithOnFaulted(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle new change password requests
|
||||
/// </summary>
|
||||
/// <param name="connectParams"></param>
|
||||
/// <param name="requestContext"></param>
|
||||
/// <returns></returns>
|
||||
protected async Task HandleChangePasswordRequest(
|
||||
ChangePasswordParams changePasswordParams,
|
||||
RequestContext<PasswordChangeResponse> requestContext)
|
||||
{
|
||||
Logger.Write(TraceEventType.Verbose, "HandleChangePasswordRequest");
|
||||
PasswordChangeResponse newResponse = new PasswordChangeResponse();
|
||||
try
|
||||
{
|
||||
ChangePassword(changePasswordParams);
|
||||
newResponse.Result = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
newResponse.Result = false;
|
||||
newResponse.ErrorMessage = ex.InnerException != null ? (ex.Message + Environment.NewLine + Environment.NewLine + ex.InnerException.Message) : ex.Message;
|
||||
newResponse.ErrorMessage = Regex.Replace(newResponse.ErrorMessage, @"\r?\nChanged database context to '\w+'\.", "");
|
||||
newResponse.ErrorMessage = Regex.Replace(newResponse.ErrorMessage, @"\r?\nChanged language setting to \w+\.", "");
|
||||
if (newResponse.ErrorMessage.Equals(SR.PasswordChangeEmptyPassword))
|
||||
{
|
||||
newResponse.ErrorMessage += Environment.NewLine + Environment.NewLine + SR.PasswordChangeEmptyPasswordRetry;
|
||||
}
|
||||
else if (newResponse.ErrorMessage.Contains(SR.PasswordChangeDNMReqs))
|
||||
{
|
||||
newResponse.ErrorMessage += Environment.NewLine + Environment.NewLine + SR.PasswordChangeDNMReqsRetry;
|
||||
}
|
||||
else if (newResponse.ErrorMessage.Contains(SR.PasswordChangePWCannotBeUsed))
|
||||
{
|
||||
newResponse.ErrorMessage += Environment.NewLine + Environment.NewLine + SR.PasswordChangePWCannotBeUsedRetry;
|
||||
}
|
||||
}
|
||||
await requestContext.SendResult(newResponse);
|
||||
}
|
||||
|
||||
public void ChangePassword(ChangePasswordParams changePasswordParams)
|
||||
{
|
||||
// Empty passwords are not valid.
|
||||
if (string.IsNullOrEmpty(changePasswordParams.NewPassword))
|
||||
{
|
||||
throw new Exception(SR.PasswordChangeEmptyPassword);
|
||||
}
|
||||
|
||||
// result is null if the ConnectParams was successfully validated
|
||||
ConnectionCompleteParams result = ValidateConnectParams(changePasswordParams);
|
||||
if (result != null)
|
||||
{
|
||||
throw new Exception(result.ErrorMessage, new Exception(result.Messages));
|
||||
}
|
||||
|
||||
// Change the password of the connection
|
||||
ServerConnection serverConnection = new ServerConnection(changePasswordParams.Connection.ServerName, changePasswordParams.Connection.UserName, changePasswordParams.Connection.Password);
|
||||
serverConnection.ChangePassword(changePasswordParams.NewPassword);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle cancel connect requests
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters for the Change Password Request.
|
||||
/// </summary>
|
||||
public class ChangePasswordParams : ConnectParams
|
||||
{
|
||||
/// <summary>
|
||||
/// The password to change the account of the connection to.
|
||||
/// </summary>
|
||||
public string NewPassword { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Connect request mapping entry
|
||||
/// </summary>
|
||||
public class ChangePasswordRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<ChangePasswordParams, PasswordChangeResponse> Type =
|
||||
RequestType<ChangePasswordParams, PasswordChangeResponse>.Create("connection/changepassword");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters to be sent back after a password change attempt.
|
||||
/// </summary>
|
||||
public class PasswordChangeResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Status indicating if password change was successful or not.
|
||||
/// </summary>
|
||||
public bool Result { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Error message for the password change, if an error occured.
|
||||
/// </summary>
|
||||
public string? ErrorMessage { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,54 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
}
|
||||
}
|
||||
|
||||
public static string PasswordChangeEmptyPassword
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.PasswordChangeEmptyPassword);
|
||||
}
|
||||
}
|
||||
|
||||
public static string PasswordChangeEmptyPasswordRetry
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.PasswordChangeEmptyPasswordRetry);
|
||||
}
|
||||
}
|
||||
|
||||
public static string PasswordChangeDNMReqs
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.PasswordChangeDNMReqs);
|
||||
}
|
||||
}
|
||||
|
||||
public static string PasswordChangeDNMReqsRetry
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.PasswordChangeDNMReqsRetry);
|
||||
}
|
||||
}
|
||||
|
||||
public static string PasswordChangePWCannotBeUsed
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.PasswordChangePWCannotBeUsed);
|
||||
}
|
||||
}
|
||||
|
||||
public static string PasswordChangePWCannotBeUsedRetry
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.PasswordChangePWCannotBeUsedRetry);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ConnectionParamsValidateNullOwnerUri
|
||||
{
|
||||
get
|
||||
@@ -10082,6 +10130,24 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
public const string ConnectionServiceConnectionCanceled = "ConnectionServiceConnectionCanceled";
|
||||
|
||||
|
||||
public const string PasswordChangeEmptyPassword = "PasswordChangeEmptyPassword";
|
||||
|
||||
|
||||
public const string PasswordChangeEmptyPasswordRetry = "PasswordChangeEmptyPasswordRetry";
|
||||
|
||||
|
||||
public const string PasswordChangeDNMReqs = "PasswordChangeDNMReqs";
|
||||
|
||||
|
||||
public const string PasswordChangeDNMReqsRetry = "PasswordChangeDNMReqsRetry";
|
||||
|
||||
|
||||
public const string PasswordChangePWCannotBeUsed = "PasswordChangePWCannotBeUsed";
|
||||
|
||||
|
||||
public const string PasswordChangePWCannotBeUsedRetry = "PasswordChangePWCannotBeUsedRetry";
|
||||
|
||||
|
||||
public const string ConnectionParamsValidateNullOwnerUri = "ConnectionParamsValidateNullOwnerUri";
|
||||
|
||||
|
||||
|
||||
@@ -168,6 +168,30 @@
|
||||
<value>Connection canceled</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="PasswordChangeEmptyPassword" xml:space="preserve">
|
||||
<value>New password cannot be empty</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="PasswordChangeEmptyPasswordRetry" xml:space="preserve">
|
||||
<value>Press OK to input a new password that is not empty.</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="PasswordChangeDNMReqs" xml:space="preserve">
|
||||
<value>password does not meet operating system policy requirements</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="PasswordChangeDNMReqsRetry" xml:space="preserve">
|
||||
<value>Press OK to input a new password that meets operating system policy requirements.</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="PasswordChangePWCannotBeUsed" xml:space="preserve">
|
||||
<value>password cannot be used at this time</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="PasswordChangePWCannotBeUsedRetry" xml:space="preserve">
|
||||
<value>Press OK to input a different password.</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="ConnectionParamsValidateNullOwnerUri" xml:space="preserve">
|
||||
<value>OwnerUri cannot be null or empty</value>
|
||||
<comment></comment>
|
||||
|
||||
@@ -45,6 +45,20 @@ ConnectionServiceConnStringInvalidIntent(string intent) = Invalid value '{0}' fo
|
||||
|
||||
ConnectionServiceConnectionCanceled = Connection canceled
|
||||
|
||||
### Password Change
|
||||
|
||||
PasswordChangeEmptyPassword = New password cannot be empty
|
||||
|
||||
PasswordChangeEmptyPasswordRetry = Press OK to input a new password that is not empty.
|
||||
|
||||
PasswordChangeDNMReqs = password does not meet operating system policy requirements
|
||||
|
||||
PasswordChangeDNMReqsRetry = Press OK to input a new password that meets operating system policy requirements.
|
||||
|
||||
PasswordChangePWCannotBeUsed = password cannot be used at this time
|
||||
|
||||
PasswordChangePWCannotBeUsedRetry = Press OK to input a different password.
|
||||
|
||||
### Connection Params Validation Errors
|
||||
|
||||
ConnectionParamsValidateNullOwnerUri = OwnerUri cannot be null or empty
|
||||
|
||||
@@ -6505,6 +6505,36 @@ The Query Processor estimates that implementing the following index could improv
|
||||
<target state="new">Actual CPU Cost</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PasswordChangeEmptyPassword">
|
||||
<source>New password cannot be empty</source>
|
||||
<target state="new">New password cannot be empty</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PasswordChangeEmptyPasswordRetry">
|
||||
<source>Press OK to input a new password that is not empty.</source>
|
||||
<target state="new">Press OK to input a new password that is not empty.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PasswordChangeDNMReqs">
|
||||
<source>password does not meet operating system policy requirements</source>
|
||||
<target state="new">password does not meet operating system policy requirements</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PasswordChangeDNMReqsRetry">
|
||||
<source>Press OK to input a new password that meets operating system policy requirements.</source>
|
||||
<target state="new">Press OK to input a new password that meets operating system policy requirements.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PasswordChangePWCannotBeUsed">
|
||||
<source>password cannot be used at this time</source>
|
||||
<target state="new">password cannot be used at this time</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PasswordChangePWCannotBeUsedRetry">
|
||||
<source>Press OK to input a different password.</source>
|
||||
<target state="new">Press OK to input a different password.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -1763,5 +1763,72 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Connection
|
||||
Assert.IsFalse(ConnectionService.IsDbPool("db"));
|
||||
Assert.IsFalse(ConnectionService.IsDbPool(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that providing an empty password to change password will fire an error.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ConnectionEmptyPasswordChange()
|
||||
{
|
||||
var serviceHostMock = new Mock<IProtocolEndpoint>();
|
||||
|
||||
var connectionService = ConnectionService.Instance;
|
||||
connectionService.ServiceHost = serviceHostMock.Object;
|
||||
|
||||
// Set up an initial connection
|
||||
const string ownerUri = "file://my/sample/file.sql";
|
||||
ChangePasswordParams testConnectionParams = new ChangePasswordParams()
|
||||
{
|
||||
OwnerUri = ownerUri,
|
||||
Connection = TestObjects.GetTestConnectionDetails(),
|
||||
NewPassword = ""
|
||||
};
|
||||
Assert.Throws<Exception>(() => connectionService.ChangePassword(testConnectionParams));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that providing an invalid connection parameter value to change password will fire an error.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ConnectionInvalidParamPasswordChange()
|
||||
{
|
||||
var serviceHostMock = new Mock<IProtocolEndpoint>();
|
||||
|
||||
var connectionService = ConnectionService.Instance;
|
||||
connectionService.ServiceHost = serviceHostMock.Object;
|
||||
|
||||
// Set up an initial connection
|
||||
const string ownerUri = "file://my/sample/file.sql";
|
||||
ChangePasswordParams testConnectionParams = new ChangePasswordParams()
|
||||
{
|
||||
OwnerUri = ownerUri,
|
||||
Connection = { },
|
||||
NewPassword = "TestPassword"
|
||||
};
|
||||
Assert.Throws<Exception>(() => connectionService.ChangePassword(testConnectionParams));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that providing a non actual connection and a fake password to change password will throw an error.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task InvalidConnectionPasswordChange()
|
||||
{
|
||||
var serviceHostMock = new Mock<IProtocolEndpoint>();
|
||||
|
||||
var connectionService = ConnectionService.Instance;
|
||||
connectionService.ServiceHost = serviceHostMock.Object;
|
||||
|
||||
// Set up an initial connection
|
||||
const string ownerUri = "file://my/sample/file.sql";
|
||||
ChangePasswordParams testConnectionParams = new ChangePasswordParams()
|
||||
{
|
||||
OwnerUri = ownerUri,
|
||||
Connection = TestObjects.GetTestConnectionDetails(),
|
||||
NewPassword = "TestPassword"
|
||||
};
|
||||
Assert.Throws<Microsoft.SqlServer.Management.Common.ChangePasswordFailureException>(
|
||||
() => connectionService.ChangePassword(testConnectionParams));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user