mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 10:58:30 -05:00
Adding network share validation in sql migration. (#1203)
* adding network file validator contract * Adding UNC path validation * Using helper methods for sanity checks * Fixed the validation variable type * Fixing if condition
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// 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.Migration.Contracts
|
||||||
|
{
|
||||||
|
public class ValidateNetworkFileShareRequestParams
|
||||||
|
{
|
||||||
|
public string Path { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ValidateNetworkFileShareRequest
|
||||||
|
{
|
||||||
|
public static readonly RequestType<ValidateNetworkFileShareRequestParams, bool> Type = RequestType<ValidateNetworkFileShareRequestParams, bool>.Create("migration/validateNetworkFileShare");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,14 +13,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration.Contracts
|
|||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ValidateWindowsAccountResult
|
|
||||||
{
|
|
||||||
public bool Success { get; set; }
|
|
||||||
public string ErrorMessage { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ValidateWindowsAccountRequest
|
public class ValidateWindowsAccountRequest
|
||||||
{
|
{
|
||||||
public static readonly RequestType<ValidateWindowsAccountRequestParams, ValidateWindowsAccountResult> Type = RequestType<ValidateWindowsAccountRequestParams, ValidateWindowsAccountResult>.Create("migration/validateWindowsAccount");
|
public static readonly RequestType<ValidateWindowsAccountRequestParams, bool> Type = RequestType<ValidateWindowsAccountRequestParams, bool>.Create("migration/validateWindowsAccount");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,8 @@ using Microsoft.Win32.SafeHandles;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using System.IO;
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.Migration
|
namespace Microsoft.SqlTools.ServiceLayer.Migration
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -93,6 +94,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
this.ServiceHost = serviceHost;
|
this.ServiceHost = serviceHost;
|
||||||
this.ServiceHost.SetRequestHandler(MigrationAssessmentsRequest.Type, HandleMigrationAssessmentsRequest);
|
this.ServiceHost.SetRequestHandler(MigrationAssessmentsRequest.Type, HandleMigrationAssessmentsRequest);
|
||||||
this.ServiceHost.SetRequestHandler(ValidateWindowsAccountRequest.Type, HandleValidateWindowsAccountRequest);
|
this.ServiceHost.SetRequestHandler(ValidateWindowsAccountRequest.Type, HandleValidateWindowsAccountRequest);
|
||||||
|
this.ServiceHost.SetRequestHandler(ValidateNetworkFileShareRequest.Type, HandleValidateNetworkFileShareRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -259,24 +261,18 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
|
|
||||||
internal async Task HandleValidateWindowsAccountRequest(
|
internal async Task HandleValidateWindowsAccountRequest(
|
||||||
ValidateWindowsAccountRequestParams parameters,
|
ValidateWindowsAccountRequestParams parameters,
|
||||||
RequestContext<ValidateWindowsAccountResult> requestContext)
|
RequestContext<bool> requestContext)
|
||||||
{
|
{
|
||||||
var domainUserRegex = new Regex(@"^[A-Za-z0-9\\\._-]{7,}$");
|
if (!ValidateWindowsDomainUsername(parameters.Username))
|
||||||
// Checking if the username string is in 'domain\name' format
|
|
||||||
if (!domainUserRegex.Match(parameters.Username).Success)
|
|
||||||
{
|
{
|
||||||
await requestContext.SendResult(new ValidateWindowsAccountResult()
|
await requestContext.SendError("Invalid user name format. Example: Domain\\username");
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
ErrorMessage = "Invalid user name format. Example: Domain\\username"
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
int separator = parameters.Username.IndexOf("\\");
|
int separator = parameters.Username.IndexOf("\\");
|
||||||
string domainName = (separator > -1) ? parameters.Username.Substring(0, separator) : string.Empty;
|
string domainName = parameters.Username.Substring(0, separator);
|
||||||
string userName = (separator > -1) ? parameters.Username.Substring(separator + 1, parameters.Username.Length - separator - 1) : string.Empty;
|
string userName = parameters.Username.Substring(separator + 1, parameters.Username.Length - separator - 1);
|
||||||
|
|
||||||
const int LOGON32_PROVIDER_DEFAULT = 0;
|
const int LOGON32_PROVIDER_DEFAULT = 0;
|
||||||
const int LOGON32_LOGON_INTERACTIVE = 2;
|
const int LOGON32_LOGON_INTERACTIVE = 2;
|
||||||
@@ -286,23 +282,102 @@ namespace Microsoft.SqlTools.ServiceLayer.Migration
|
|||||||
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
|
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
|
||||||
out safeAccessTokenHandle);
|
out safeAccessTokenHandle);
|
||||||
|
|
||||||
if (false == returnValue)
|
if (!returnValue)
|
||||||
{
|
{
|
||||||
int ret = Marshal.GetLastWin32Error();
|
int ret = Marshal.GetLastWin32Error();
|
||||||
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
|
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
|
||||||
await requestContext.SendResult(new ValidateWindowsAccountResult()
|
await requestContext.SendError(errorMessage);
|
||||||
{
|
}
|
||||||
Success = returnValue,
|
else
|
||||||
ErrorMessage = errorMessage
|
{
|
||||||
});
|
await requestContext.SendResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await requestContext.SendResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task HandleValidateNetworkFileShareRequest(
|
||||||
|
ValidateNetworkFileShareRequestParams parameters,
|
||||||
|
RequestContext<bool> requestContext)
|
||||||
|
{
|
||||||
|
if (!ValidateWindowsDomainUsername(parameters.Username))
|
||||||
|
{
|
||||||
|
await requestContext.SendError("Invalid user name format. Example: Domain\\username");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ValidateUNCPath(parameters.Path))
|
||||||
|
{
|
||||||
|
await requestContext.SendError("Invalid network share path. Example: \\\\Servername.domainname.com\\Backupfolder");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
int separator = parameters.Username.IndexOf("\\");
|
||||||
|
string domainName = parameters.Username.Substring(0, separator);
|
||||||
|
string userName = parameters.Username.Substring(separator + 1, parameters.Username.Length - separator - 1);
|
||||||
|
|
||||||
|
const int LOGON32_PROVIDER_WINNT50 = 3;
|
||||||
|
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
|
||||||
|
|
||||||
|
SafeAccessTokenHandle safeAccessTokenHandle;
|
||||||
|
bool returnValue = LogonUser(
|
||||||
|
userName,
|
||||||
|
domainName,
|
||||||
|
parameters.Password,
|
||||||
|
LOGON32_LOGON_NEW_CREDENTIALS,
|
||||||
|
LOGON32_PROVIDER_WINNT50,
|
||||||
|
out safeAccessTokenHandle);
|
||||||
|
|
||||||
|
if (!returnValue)
|
||||||
|
{
|
||||||
|
int ret = Marshal.GetLastWin32Error();
|
||||||
|
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
|
||||||
|
await requestContext.SendError(errorMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
await WindowsIdentity.RunImpersonated(
|
||||||
await requestContext.SendResult(new ValidateWindowsAccountResult()
|
safeAccessTokenHandle,
|
||||||
|
// User action
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
if(!Directory.Exists(parameters.Path)){
|
||||||
|
await requestContext.SendError("Cannot connect to file share");
|
||||||
|
} else {
|
||||||
|
await requestContext.SendResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Success = true,
|
await requestContext.SendResult(true);
|
||||||
ErrorMessage = ""
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the username is in 'domain\username' format.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal bool ValidateWindowsDomainUsername(string username)
|
||||||
|
{
|
||||||
|
var domainUserRegex = new Regex(@"^(?<domain>[A-Za-z0-9\._-]*)\\(?<username>[A-Za-z0-9\._-]*)$");
|
||||||
|
return domainUserRegex.IsMatch(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the file path is in UNC format '\\Servername.domainname.com\Backupfolder'
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal bool ValidateUNCPath(string path)
|
||||||
|
{
|
||||||
|
return new Uri(path).IsUnc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user