mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 10:58:30 -05:00
Support creating & editing additional user types (#1962)
* WIP * Fix contained user password handling * More user related bug fixes
This commit is contained in:
@@ -4,7 +4,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -245,8 +244,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
|||||||
databases[i] = dataContainer.Server.Databases[i].Name;
|
databases[i] = dataContainer.Server.Databases[i].Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
var languageOptions = GetDefaultLanguageOptions(dataContainer);
|
var languageOptions = LanguageUtils.GetDefaultLanguageOptions(dataContainer);
|
||||||
var languageOptionsList = languageOptions.Select(FormatLanguageDisplay).ToList();
|
var languageOptionsList = languageOptions.Select(SecurityService.FormatLanguageDisplay).ToList();
|
||||||
if (parameters.IsNewObject)
|
if (parameters.IsNewObject)
|
||||||
{
|
{
|
||||||
languageOptionsList.Insert(0, SR.DefaultLanguagePlaceholder);
|
languageOptionsList.Insert(0, SR.DefaultLanguagePlaceholder);
|
||||||
@@ -320,36 +319,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
|||||||
await requestContext.SendResult(new object());
|
await requestContext.SendResult(new object());
|
||||||
}
|
}
|
||||||
|
|
||||||
private string FormatLanguageDisplay(LanguageDisplay? l)
|
internal static string FormatLanguageDisplay(LanguageDisplay? l)
|
||||||
{
|
{
|
||||||
if (l == null) return null;
|
if (l == null) return null;
|
||||||
return string.Format("{0} - {1}", l.Language.Alias, l.Language.Name);
|
return string.Format("{0} - {1}", l.Language.Alias, l.Language.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IList<LanguageDisplay> GetDefaultLanguageOptions(CDataContainer dataContainer)
|
|
||||||
{
|
|
||||||
// sort the languages alphabetically by alias
|
|
||||||
SortedList sortedLanguages = new SortedList(Comparer.Default);
|
|
||||||
|
|
||||||
LanguageUtils.SetLanguageDefaultInitFieldsForDefaultLanguages(dataContainer.Server);
|
|
||||||
if (dataContainer.Server != null && dataContainer.Server.Languages != null)
|
|
||||||
{
|
|
||||||
foreach (Language language in dataContainer.Server.Languages)
|
|
||||||
{
|
|
||||||
LanguageDisplay listValue = new LanguageDisplay(language);
|
|
||||||
sortedLanguages.Add(language.Alias, listValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IList<LanguageDisplay> res = new List<LanguageDisplay>();
|
|
||||||
foreach (LanguageDisplay ld in sortedLanguages.Values)
|
|
||||||
{
|
|
||||||
res.Add(ld);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "Credential Handlers"
|
#region "Credential Handlers"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using Microsoft.SqlServer.Management.Common;
|
using Microsoft.SqlServer.Management.Common;
|
||||||
@@ -101,42 +102,47 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
|||||||
CDataContainer dataContainer = CreateUserDataContainer(connInfo, null, ConfigAction.Create, parameters.Database);
|
CDataContainer dataContainer = CreateUserDataContainer(connInfo, null, ConfigAction.Create, parameters.Database);
|
||||||
string databaseUrn = string.Format(System.Globalization.CultureInfo.InvariantCulture,
|
string databaseUrn = string.Format(System.Globalization.CultureInfo.InvariantCulture,
|
||||||
"Server/Database[@Name='{0}']", Urn.EscapeString(parameters.Database));
|
"Server/Database[@Name='{0}']", Urn.EscapeString(parameters.Database));
|
||||||
Database? parentDb = dataContainer.Server.GetSmoObject(databaseUrn) as Database;
|
Database? parentDb = dataContainer.Server.GetSmoObject(databaseUrn) as Database;
|
||||||
|
|
||||||
|
var languageOptions = LanguageUtils.GetDefaultLanguageOptions(dataContainer);
|
||||||
|
var languageOptionsList = languageOptions.Select(SecurityService.FormatLanguageDisplay).ToList();
|
||||||
|
languageOptionsList.Insert(0, SR.DefaultLanguagePlaceholder);
|
||||||
|
|
||||||
// if viewing an exisitng user then populate some properties
|
// if viewing an exisitng user then populate some properties
|
||||||
UserInfo? userInfo = null;
|
UserInfo? userInfo = null;
|
||||||
|
string? defaultLanguageAlias = null;
|
||||||
ExhaustiveUserTypes userType = ExhaustiveUserTypes.LoginMappedUser;
|
ExhaustiveUserTypes userType = ExhaustiveUserTypes.LoginMappedUser;
|
||||||
if (!parameters.IsNewObject)
|
if (!parameters.IsNewObject)
|
||||||
{
|
{
|
||||||
User? existingUser = dataContainer.Server.Databases[parentDb.Name].Users[parameters.Name];
|
User existingUser = dataContainer.Server.Databases[parentDb.Name].Users[parameters.Name];
|
||||||
userType = UserActions.GetCurrentUserTypeForExistingUser(existingUser);
|
userType = UserActions.GetCurrentUserTypeForExistingUser(existingUser);
|
||||||
|
DatabaseUserType databaseUserType = UserActions.GetDatabaseUserTypeForUserType(userType);
|
||||||
userInfo = new UserInfo()
|
userInfo = new UserInfo()
|
||||||
{
|
{
|
||||||
|
Type = databaseUserType,
|
||||||
Name = parameters.Name,
|
Name = parameters.Name,
|
||||||
LoginName = existingUser.Login,
|
LoginName = existingUser.Login,
|
||||||
DefaultSchema = existingUser.DefaultSchema
|
DefaultSchema = existingUser.DefaultSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// update the authentication type for contained users
|
||||||
|
if (databaseUserType == DatabaseUserType.Contained)
|
||||||
|
{
|
||||||
|
userInfo.AuthenticationType = ServerAuthenticationType.Sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default language is only applicable for users inside a contained database.
|
||||||
|
if (parentDb.ContainmentType != ContainmentType.None
|
||||||
|
&& LanguageUtils.IsDefaultLanguageSupported(dataContainer.Server))
|
||||||
|
{
|
||||||
|
defaultLanguageAlias = LanguageUtils.GetLanguageAliasFromName(
|
||||||
|
existingUser.Parent.Parent,
|
||||||
|
existingUser.DefaultLanguage.Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate a user prototype
|
// generate a user prototype
|
||||||
UserPrototype currentUserPrototype = UserPrototypeFactory.GetUserPrototype(dataContainer, userInfo, originalData: null, userType);
|
UserPrototype currentUserPrototype = UserPrototypeFactory.GetUserPrototype(dataContainer, userInfo, originalData: null, userType);
|
||||||
|
|
||||||
// get the default language if available
|
|
||||||
IUserPrototypeWithDefaultLanguage defaultLanguagePrototype = currentUserPrototype as IUserPrototypeWithDefaultLanguage;
|
|
||||||
string? defaultLanguageAlias = null;
|
|
||||||
if (defaultLanguagePrototype != null && defaultLanguagePrototype.IsDefaultLanguageSupported)
|
|
||||||
{
|
|
||||||
string dbUrn = "Server/Database[@Name='" + Urn.EscapeString(parameters.Database) + "']";
|
|
||||||
defaultLanguageAlias = defaultLanguagePrototype.DefaultLanguageAlias;
|
|
||||||
//If engine returns default language as empty or null, that means the default language of
|
|
||||||
//database will be used.
|
|
||||||
//Default language is not applicable for users inside an uncontained authentication.
|
|
||||||
if (string.IsNullOrEmpty(defaultLanguageAlias)
|
|
||||||
&& (dataContainer.Server.GetSmoObject(dbUrn) as Database).ContainmentType != ContainmentType.None)
|
|
||||||
{
|
|
||||||
defaultLanguageAlias = SR.DefaultLanguagePlaceholder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the default schema if available
|
// get the default schema if available
|
||||||
string? defaultSchema = null;
|
string? defaultSchema = null;
|
||||||
@@ -145,13 +151,24 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
|||||||
{
|
{
|
||||||
defaultSchema = defaultSchemaPrototype.DefaultSchema;
|
defaultSchema = defaultSchemaPrototype.DefaultSchema;
|
||||||
}
|
}
|
||||||
|
|
||||||
// IUserPrototypeWithPassword userWithPwdPrototype = currentUserPrototype as IUserPrototypeWithPassword;
|
// set default alias to <default> if needed
|
||||||
// if (userWithPwdPrototype != null && !this.DataContainer.IsNewObject)
|
if (string.IsNullOrEmpty(defaultLanguageAlias)
|
||||||
// {
|
&& parentDb.ContainmentType != ContainmentType.None
|
||||||
// this.passwordTextBox.Text = FAKE_PASSWORD;
|
&& LanguageUtils.IsDefaultLanguageSupported(dataContainer.Server))
|
||||||
// this.confirmPwdTextBox.Text = FAKE_PASSWORD;
|
{
|
||||||
// }
|
defaultLanguageAlias = SR.DefaultLanguagePlaceholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the fake password placeholder when editing an existing user
|
||||||
|
string? password = null;
|
||||||
|
IUserPrototypeWithPassword userWithPwdPrototype = currentUserPrototype as IUserPrototypeWithPassword;
|
||||||
|
if (userWithPwdPrototype != null && !parameters.IsNewObject)
|
||||||
|
{
|
||||||
|
userWithPwdPrototype.Password = DatabaseUtils.GetReadOnlySecureString(LoginPrototype.fakePassword);
|
||||||
|
userWithPwdPrototype.PasswordConfirm = DatabaseUtils.GetReadOnlySecureString(LoginPrototype.fakePassword);
|
||||||
|
password = LoginPrototype.fakePassword;
|
||||||
|
}
|
||||||
|
|
||||||
// get the login name if it exists
|
// get the login name if it exists
|
||||||
string? loginName = null;
|
string? loginName = null;
|
||||||
@@ -180,26 +197,28 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
|||||||
schemaNames.Add(schema);
|
schemaNames.Add(schema);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerConnection serverConnection = dataContainer.ServerConnection;
|
ServerConnection serverConnection = dataContainer.ServerConnection;
|
||||||
UserViewInfo userViewInfo = new UserViewInfo()
|
UserViewInfo userViewInfo = new UserViewInfo()
|
||||||
{
|
{
|
||||||
ObjectInfo = new UserInfo()
|
ObjectInfo = new UserInfo()
|
||||||
{
|
{
|
||||||
Type = DatabaseUserType.WithLogin,
|
Type = userInfo?.Type ?? DatabaseUserType.WithLogin,
|
||||||
|
AuthenticationType = userInfo?.AuthenticationType ?? ServerAuthenticationType.Sql,
|
||||||
Name = currentUserPrototype.Name,
|
Name = currentUserPrototype.Name,
|
||||||
LoginName = loginName,
|
LoginName = loginName,
|
||||||
Password = string.Empty,
|
Password = password,
|
||||||
DefaultSchema = defaultSchema,
|
DefaultSchema = defaultSchema,
|
||||||
OwnedSchemas = schemaNames.ToArray(),
|
OwnedSchemas = schemaNames.ToArray(),
|
||||||
DatabaseRoles = databaseRoles.ToArray(),
|
DatabaseRoles = databaseRoles.ToArray(),
|
||||||
DefaultLanguage = defaultLanguageAlias
|
DefaultLanguage = SecurityService.FormatLanguageDisplay(
|
||||||
|
languageOptions.FirstOrDefault(o => o?.Language.Name == defaultLanguageAlias || o?.Language.Alias == defaultLanguageAlias, null)),
|
||||||
},
|
},
|
||||||
SupportContainedUser = UserActions.IsParentDatabaseContained(parentDb), // support for these will be added later
|
SupportContainedUser = UserActions.IsParentDatabaseContained(parentDb), // support for these will be added later
|
||||||
SupportWindowsAuthentication = false,
|
SupportWindowsAuthentication = false,
|
||||||
SupportAADAuthentication = false,
|
SupportAADAuthentication = false,
|
||||||
SupportSQLAuthentication = true,
|
SupportSQLAuthentication = true,
|
||||||
Languages = new string[] { },
|
Languages = languageOptionsList.ToArray(),
|
||||||
Schemas = currentUserPrototype.SchemaNames.ToArray(),
|
Schemas = currentUserPrototype.SchemaNames.ToArray(),
|
||||||
Logins = DatabaseUtils.LoadSqlLogins(serverConnection),
|
Logins = DatabaseUtils.LoadSqlLogins(serverConnection),
|
||||||
DatabaseRoles = currentUserPrototype.DatabaseRoleNames.ToArray()
|
DatabaseRoles = currentUserPrototype.DatabaseRoleNames.ToArray()
|
||||||
@@ -363,28 +382,35 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
|||||||
internal class UserActions : ManagementActionBase
|
internal class UserActions : ManagementActionBase
|
||||||
{
|
{
|
||||||
#region Variables
|
#region Variables
|
||||||
//private UserPrototypeData userData;
|
|
||||||
private UserPrototype userPrototype;
|
private UserPrototype userPrototype;
|
||||||
private UserInfo? user;
|
|
||||||
private ConfigAction configAction;
|
private ConfigAction configAction;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructors / Dispose
|
#region Constructors / Dispose
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// required when loading from Object Explorer context
|
/// Handle user create and update actions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
|
||||||
public UserActions(
|
public UserActions(
|
||||||
CDataContainer context,
|
CDataContainer dataContainer,
|
||||||
ConfigAction configAction,
|
ConfigAction configAction,
|
||||||
UserInfo? user,
|
UserInfo? user,
|
||||||
UserPrototypeData? originalData)
|
UserPrototypeData? originalData)
|
||||||
{
|
{
|
||||||
this.DataContainer = context;
|
this.DataContainer = dataContainer;
|
||||||
this.user = user;
|
|
||||||
this.configAction = configAction;
|
this.configAction = configAction;
|
||||||
|
|
||||||
this.userPrototype = InitUserPrototype(context, user, originalData);
|
ExhaustiveUserTypes currentUserType;
|
||||||
|
if (dataContainer.IsNewObject)
|
||||||
|
{
|
||||||
|
currentUserType = UserActions.GetUserTypeForUserInfo(user);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentUserType = UserActions.GetCurrentUserTypeForExistingUser(
|
||||||
|
dataContainer.Server.GetSmoObject(dataContainer.ObjectUrn) as User);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.userPrototype = UserPrototypeFactory.GetUserPrototype(dataContainer, user, originalData, currentUserType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// <summary>
|
// /// <summary>
|
||||||
@@ -409,24 +435,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserPrototype InitUserPrototype(CDataContainer dataContainer, UserInfo user, UserPrototypeData? originalData)
|
internal static ExhaustiveUserTypes GetUserTypeForUserInfo(UserInfo user)
|
||||||
{
|
|
||||||
ExhaustiveUserTypes currentUserType;
|
|
||||||
if (dataContainer.IsNewObject)
|
|
||||||
{
|
|
||||||
currentUserType = GetUserTypeForUserInfo(user);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentUserType = UserActions.GetCurrentUserTypeForExistingUser(
|
|
||||||
dataContainer.Server.GetSmoObject(dataContainer.ObjectUrn) as User);
|
|
||||||
}
|
|
||||||
|
|
||||||
UserPrototype currentUserPrototype = UserPrototypeFactory.GetUserPrototype(dataContainer, user, originalData, currentUserType);
|
|
||||||
return currentUserPrototype;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExhaustiveUserTypes GetUserTypeForUserInfo(UserInfo user)
|
|
||||||
{
|
{
|
||||||
ExhaustiveUserTypes userType = ExhaustiveUserTypes.LoginMappedUser;
|
ExhaustiveUserTypes userType = ExhaustiveUserTypes.LoginMappedUser;
|
||||||
switch (user.Type)
|
switch (user.Type)
|
||||||
@@ -447,6 +456,27 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
|||||||
return userType;
|
return userType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static DatabaseUserType GetDatabaseUserTypeForUserType(ExhaustiveUserTypes userType)
|
||||||
|
{
|
||||||
|
DatabaseUserType databaseUserType = DatabaseUserType.WithLogin;
|
||||||
|
switch (userType)
|
||||||
|
{
|
||||||
|
case ExhaustiveUserTypes.LoginMappedUser:
|
||||||
|
databaseUserType = DatabaseUserType.WithLogin;
|
||||||
|
break;
|
||||||
|
case ExhaustiveUserTypes.WindowsUser:
|
||||||
|
databaseUserType = DatabaseUserType.WithWindowsGroupLogin;
|
||||||
|
break;
|
||||||
|
case ExhaustiveUserTypes.SqlUserWithPassword:
|
||||||
|
databaseUserType = DatabaseUserType.Contained;
|
||||||
|
break;
|
||||||
|
case ExhaustiveUserTypes.SqlUserWithoutLogin:
|
||||||
|
databaseUserType = DatabaseUserType.NoConnectAccess;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return databaseUserType;
|
||||||
|
}
|
||||||
|
|
||||||
internal static ExhaustiveUserTypes GetCurrentUserTypeForExistingUser(User? user)
|
internal static ExhaustiveUserTypes GetCurrentUserTypeForExistingUser(User? user)
|
||||||
{
|
{
|
||||||
if (user == null)
|
if (user == null)
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
|
using Microsoft.SqlServer.Management.Common;
|
||||||
using Microsoft.SqlServer.Management.Smo;
|
using Microsoft.SqlServer.Management.Smo;
|
||||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Security.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.Security.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||||
using Microsoft.SqlServer.Management.Common;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.Security
|
namespace Microsoft.SqlTools.ServiceLayer.Security
|
||||||
{
|
{
|
||||||
@@ -122,9 +122,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
|||||||
this.mappedLoginName = userInfo.LoginName;
|
this.mappedLoginName = userInfo.LoginName;
|
||||||
this.defaultSchemaName = userInfo.DefaultSchema;
|
this.defaultSchemaName = userInfo.DefaultSchema;
|
||||||
if (!string.IsNullOrEmpty(userInfo.Password))
|
if (!string.IsNullOrEmpty(userInfo.Password))
|
||||||
{
|
{
|
||||||
this.password = DatabaseUtils.GetReadOnlySecureString(userInfo.Password);
|
this.password = DatabaseUtils.GetReadOnlySecureString(userInfo.Password);
|
||||||
}
|
}
|
||||||
|
if (!string.IsNullOrEmpty(userInfo.DefaultLanguage)
|
||||||
|
&& string.Compare(userInfo.DefaultLanguage, SR.DefaultLanguagePlaceholder, StringComparison.Ordinal) != 0)
|
||||||
|
{
|
||||||
|
this.defaultLanguageAlias = LanguageUtils.GetLanguageAliasFromDisplayText(userInfo.DefaultLanguage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.LoadRoleMembership(context, userInfo);
|
this.LoadRoleMembership(context, userInfo);
|
||||||
@@ -460,7 +465,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
|||||||
public void SetRoleMembership(string roleName, bool isMember)
|
public void SetRoleMembership(string roleName, bool isMember)
|
||||||
{
|
{
|
||||||
this.currentState.isMember[roleName] = isMember;
|
this.currentState.isMember[roleName] = isMember;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -800,9 +805,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
//Default Language was not supported before Denali.
|
return LanguageUtils.IsDefaultLanguageSupported(this.context.Server);
|
||||||
return SqlMgmtUtils.IsSql11OrLater(this.context.Server.ConnectionContext.ServerVersion)
|
|
||||||
&& this.context.Server.ServerType != DatabaseEngineType.SqlAzureDatabase;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -970,7 +973,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
|||||||
{
|
{
|
||||||
user.Alter();
|
user.Alter();
|
||||||
|
|
||||||
if (this.currentState.password != this.originalState.password)
|
if (!DatabaseUtils.IsSecureStringsEqual(this.currentState.password, this.originalState.password))
|
||||||
{
|
{
|
||||||
if (this.currentState.isOldPasswordRequired)
|
if (this.currentState.isOldPasswordRequired)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
||||||
@@ -86,6 +87,45 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
|
|||||||
return ss;
|
return ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsSecureStringsEqual(SecureString ss1, SecureString ss2)
|
||||||
|
{
|
||||||
|
IntPtr bstr1 = IntPtr.Zero;
|
||||||
|
IntPtr bstr2 = IntPtr.Zero;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bstr1 = Marshal.SecureStringToBSTR(ss1);
|
||||||
|
bstr2 = Marshal.SecureStringToBSTR(ss2);
|
||||||
|
int length1 = Marshal.ReadInt32(bstr1, -4);
|
||||||
|
int length2 = Marshal.ReadInt32(bstr2, -4);
|
||||||
|
if (length1 != length2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int x = 0; x < length1; ++x)
|
||||||
|
{
|
||||||
|
byte b1 = Marshal.ReadByte(bstr1, x);
|
||||||
|
byte b2 = Marshal.ReadByte(bstr2, x);
|
||||||
|
if (b1 != b2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (bstr2 != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Marshal.ZeroFreeBSTR(bstr2);
|
||||||
|
}
|
||||||
|
if (bstr1 != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Marshal.ZeroFreeBSTR(bstr1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// this is the main method that is called by DropAllObjects for every object
|
/// this is the main method that is called by DropAllObjects for every object
|
||||||
/// in the grid
|
/// in the grid
|
||||||
|
|||||||
@@ -4,13 +4,16 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Microsoft.SqlServer.Management.Common;
|
using Microsoft.SqlServer.Management.Common;
|
||||||
using Microsoft.SqlServer.Management.Smo;
|
using Microsoft.SqlServer.Management.Smo;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
namespace Microsoft.SqlTools.ServiceLayer.Utility
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Summary description for CUtils.
|
/// Utility functions for working with server languages
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class LanguageUtils
|
internal class LanguageUtils
|
||||||
{
|
{
|
||||||
@@ -20,8 +23,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
|
|||||||
/// <param name="connectedServer"></param>
|
/// <param name="connectedServer"></param>
|
||||||
/// <param name="languageName"></param>
|
/// <param name="languageName"></param>
|
||||||
/// <returns>Returns string.Empty in case it doesn't find a matching languageName on the server</returns>
|
/// <returns>Returns string.Empty in case it doesn't find a matching languageName on the server</returns>
|
||||||
public static string GetLanguageAliasFromName(Server connectedServer,
|
public static string GetLanguageAliasFromName(Server connectedServer, string languageName)
|
||||||
string languageName)
|
|
||||||
{
|
{
|
||||||
string languageAlias = string.Empty;
|
string languageAlias = string.Empty;
|
||||||
|
|
||||||
@@ -45,8 +47,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
|
|||||||
/// <param name="connectedServer"></param>
|
/// <param name="connectedServer"></param>
|
||||||
/// <param name="languageAlias"></param>
|
/// <param name="languageAlias"></param>
|
||||||
/// <returns>Returns string.Empty in case it doesn't find a matching languageAlias on the server</returns>
|
/// <returns>Returns string.Empty in case it doesn't find a matching languageAlias on the server</returns>
|
||||||
public static string GetLanguageNameFromAlias(Server connectedServer,
|
public static string GetLanguageNameFromAlias(Server connectedServer, string languageAlias)
|
||||||
string languageAlias)
|
|
||||||
{
|
{
|
||||||
string languageName = string.Empty;
|
string languageName = string.Empty;
|
||||||
|
|
||||||
@@ -70,8 +71,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
|
|||||||
/// <param name="connectedServer"></param>
|
/// <param name="connectedServer"></param>
|
||||||
/// <param name="languageAlias"></param>
|
/// <param name="languageAlias"></param>
|
||||||
/// <returns>Throws exception in case it doesn't find a matching languageId on the server</returns>
|
/// <returns>Throws exception in case it doesn't find a matching languageId on the server</returns>
|
||||||
public static int GetLcidFromLangId(Server connectedServer,
|
public static int GetLcidFromLangId(Server connectedServer, int langId)
|
||||||
int langId)
|
|
||||||
{
|
{
|
||||||
int lcid = -1; //Unacceptable Lcid.
|
int lcid = -1; //Unacceptable Lcid.
|
||||||
|
|
||||||
@@ -100,8 +100,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
|
|||||||
/// <param name="connectedServer"></param>
|
/// <param name="connectedServer"></param>
|
||||||
/// <param name="languageAlias"></param>
|
/// <param name="languageAlias"></param>
|
||||||
/// <returns>Throws exception in case it doesn't find a matching lcid on the server</returns>
|
/// <returns>Throws exception in case it doesn't find a matching lcid on the server</returns>
|
||||||
public static int GetLangIdFromLcid(Server connectedServer,
|
public static int GetLangIdFromLcid(Server connectedServer, int lcid)
|
||||||
int lcid)
|
|
||||||
{
|
{
|
||||||
int langId = -1; //Unacceptable LangId.
|
int langId = -1; //Unacceptable LangId.
|
||||||
|
|
||||||
@@ -129,8 +128,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="langid"></param>
|
/// <param name="langid"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static LanguageChoice GetLanguageChoiceAlias(Server connectedServer,
|
public static LanguageChoice GetLanguageChoiceAlias(Server connectedServer, int lcid)
|
||||||
int lcid)
|
|
||||||
{
|
{
|
||||||
SetLanguageDefaultInitFieldsForDefaultLanguages(connectedServer);
|
SetLanguageDefaultInitFieldsForDefaultLanguages(connectedServer);
|
||||||
|
|
||||||
@@ -155,17 +153,46 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
|
|||||||
string[] fieldsNeeded = new string[] { "Alias", "Name", "LocaleID", "LangID" };
|
string[] fieldsNeeded = new string[] { "Alias", "Name", "LocaleID", "LangID" };
|
||||||
connectedServer.SetDefaultInitFields(typeof(Language), fieldsNeeded);
|
connectedServer.SetDefaultInitFields(typeof(Language), fieldsNeeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IList<LanguageDisplay> GetDefaultLanguageOptions(CDataContainer dataContainer)
|
||||||
|
{
|
||||||
|
// sort the languages alphabetically by alias
|
||||||
|
SortedList sortedLanguages = new SortedList(Comparer.Default);
|
||||||
|
|
||||||
|
LanguageUtils.SetLanguageDefaultInitFieldsForDefaultLanguages(dataContainer.Server);
|
||||||
|
if (dataContainer.Server != null && dataContainer.Server.Languages != null)
|
||||||
|
{
|
||||||
|
foreach (Language language in dataContainer.Server.Languages)
|
||||||
|
{
|
||||||
|
LanguageDisplay listValue = new LanguageDisplay(language);
|
||||||
|
sortedLanguages.Add(language.Alias, listValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IList<LanguageDisplay> res = new List<LanguageDisplay>();
|
||||||
|
foreach (LanguageDisplay ld in sortedLanguages.Values)
|
||||||
|
{
|
||||||
|
res.Add(ld);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetLanguageAliasFromDisplayText(string? displayText)
|
||||||
|
{
|
||||||
|
string[] parts = displayText?.Split(" - ");
|
||||||
|
return (parts != null && parts.Length > 1) ? parts[0] : displayText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsDefaultLanguageSupported(Server server)
|
||||||
|
{
|
||||||
|
//Default Language was not supported before Denali.
|
||||||
|
return SqlMgmtUtils.IsSql11OrLater(server.ConnectionContext.ServerVersion)
|
||||||
|
&& server.ServerType != DatabaseEngineType.SqlAzureDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region interface - ILanguageLcidWithConnectionInfo - used by property editors to talk with data object
|
|
||||||
interface ILanguageLcidWithConnectionInfo
|
|
||||||
{
|
|
||||||
int Lcid { get; }
|
|
||||||
ServerConnection Connection { get; }
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region class - LanguageChoice
|
|
||||||
internal class LanguageChoice
|
internal class LanguageChoice
|
||||||
{
|
{
|
||||||
public string alias;
|
public string alias;
|
||||||
@@ -181,5 +208,4 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
|
|||||||
return alias;
|
return alias;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Security
|
|||||||
IsLockedOut = false,
|
IsLockedOut = false,
|
||||||
EnforcePasswordPolicy = false,
|
EnforcePasswordPolicy = false,
|
||||||
EnforcePasswordExpiration = false,
|
EnforcePasswordExpiration = false,
|
||||||
Password = "placeholder",
|
Password = "placeholder" + new Random().NextInt64(10000000, 90000000).ToString() + "!*PLACEHOLDER",
|
||||||
OldPassword = "placeholder",
|
OldPassword = "placeholder" + new Random().NextInt64(10000000, 90000000).ToString() + "!*PLACEHOLDER",
|
||||||
DefaultLanguage = "English - us_english",
|
DefaultLanguage = "English - us_english",
|
||||||
DefaultDatabase = "master"
|
DefaultDatabase = "master"
|
||||||
};
|
};
|
||||||
@@ -66,9 +66,10 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Security
|
|||||||
return new UserInfo()
|
return new UserInfo()
|
||||||
{
|
{
|
||||||
Type = userType,
|
Type = userType,
|
||||||
|
AuthenticationType = ServerAuthenticationType.Sql,
|
||||||
Name = userName ?? "TestUserName_" + new Random().NextInt64(10000000, 90000000).ToString(),
|
Name = userName ?? "TestUserName_" + new Random().NextInt64(10000000, 90000000).ToString(),
|
||||||
LoginName = loginName,
|
LoginName = loginName,
|
||||||
Password = "placeholder",
|
Password = "placeholder" + new Random().NextInt64(10000000, 90000000).ToString() + "!*PLACEHOLDER",
|
||||||
DefaultSchema = "dbo",
|
DefaultSchema = "dbo",
|
||||||
OwnedSchemas = new string[] { "" }
|
OwnedSchemas = new string[] { "" }
|
||||||
};
|
};
|
||||||
@@ -163,7 +164,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Security
|
|||||||
TestConnectionResult connectionResult,
|
TestConnectionResult connectionResult,
|
||||||
DatabaseUserType userType,
|
DatabaseUserType userType,
|
||||||
string userName = null,
|
string userName = null,
|
||||||
string loginName = null)
|
string loginName = null,
|
||||||
|
string databaseName = "master")
|
||||||
{
|
{
|
||||||
string contextId = System.Guid.NewGuid().ToString();
|
string contextId = System.Guid.NewGuid().ToString();
|
||||||
var initializeViewRequestParams = new InitializeUserViewParams
|
var initializeViewRequestParams = new InitializeUserViewParams
|
||||||
@@ -171,7 +173,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Security
|
|||||||
ConnectionUri = connectionResult.ConnectionInfo.OwnerUri,
|
ConnectionUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||||
ContextId = contextId,
|
ContextId = contextId,
|
||||||
IsNewObject = true,
|
IsNewObject = true,
|
||||||
Database = "master"
|
Database = databaseName
|
||||||
};
|
};
|
||||||
|
|
||||||
var initializeUserViewContext = new Mock<RequestContext<UserViewInfo>>();
|
var initializeUserViewContext = new Mock<RequestContext<UserViewInfo>>();
|
||||||
|
|||||||
@@ -64,6 +64,32 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Security
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test the basic Create User method handler
|
||||||
|
/// </summary>
|
||||||
|
// [Test] - needs contained DB
|
||||||
|
public async Task TestHandleCreateUserWithContainedSqlPassword()
|
||||||
|
{
|
||||||
|
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||||
|
{
|
||||||
|
// setup
|
||||||
|
SecurityService service = new SecurityService();
|
||||||
|
UserServiceHandlerImpl userService = new UserServiceHandlerImpl();
|
||||||
|
string databaseName = "CRM";
|
||||||
|
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(databaseName, queryTempFile.FilePath);
|
||||||
|
|
||||||
|
var user = await SecurityTestUtils.CreateUser(
|
||||||
|
userService,
|
||||||
|
connectionResult,
|
||||||
|
DatabaseUserType.Contained,
|
||||||
|
userName: null,
|
||||||
|
loginName: null,
|
||||||
|
databaseName: connectionResult.ConnectionInfo.ConnectionDetails.DatabaseName);
|
||||||
|
|
||||||
|
await SecurityTestUtils.DropObject(connectionResult.ConnectionInfo.OwnerUri, SecurityTestUtils.GetUserURN(connectionResult.ConnectionInfo.ConnectionDetails.DatabaseName, user.Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test the basic Update User method handler
|
/// Test the basic Update User method handler
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user