diff --git a/src/Microsoft.SqlTools.ServiceLayer/Management/Common/DataContainer.cs b/src/Microsoft.SqlTools.ServiceLayer/Management/Common/DataContainer.cs
index b8464cde..7921d8e8 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Management/Common/DataContainer.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Management/Common/DataContainer.cs
@@ -749,7 +749,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Management
throw new InvalidOperationException();
}
- // NOTE: ServerConnection property will constuct the object if needed
+ // NOTE: ServerConnection property will construct the object if needed
m_server ??= new Server(ServerConnection);
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Security/Contracts/UserInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/Security/Contracts/UserInfo.cs
index 5ab74266..f8f0a885 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Security/Contracts/UserInfo.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Security/Contracts/UserInfo.cs
@@ -3,55 +3,40 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
-#nullable disable
-
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace Microsoft.SqlTools.ServiceLayer.Security.Contracts
{
+ [JsonConverter(typeof(StringEnumConverter))]
+ public enum ServerAuthenticationType
+ {
+ [EnumMember(Value = "Windows")]
+ Windows,
+ [EnumMember(Value = "Sql")]
+ Sql,
+ [EnumMember(Value = "AAD")]
+ AzureActiveDirectory
+ }
+
[JsonConverter(typeof(StringEnumConverter))]
public enum DatabaseUserType
{
- [EnumMember(Value = "UserWithLogin")]
- UserWithLogin,
- [EnumMember(Value = "UserWithoutLogin")]
- UserWithoutLogin
+ // User with a server level login.
+ [EnumMember(Value = "WithLogin")]
+ WithLogin,
+ // User based on a Windows user/group that has no login, but can connect to the Database Engine through membership in a Windows group.
+ [EnumMember(Value = "WithWindowsGroupLogin")]
+ WithWindowsGroupLogin,
+ // Contained user, authentication is done within the database.
+ [EnumMember(Value = "Contained")]
+ Contained,
+ // User that cannot authenticate.
+ [EnumMember(Value = "NoConnectAccess")]
+ NoConnectAccess
}
- public class ExtendedProperty
- {
-
- public string Name { get; set; }
-
- public string Value { get; set; }
- }
-
- public class SqlObject
- {
- public string Name { get; set; }
-
- public string Path { get; set; }
- }
-
- public class Permission
- {
- public string Name { get; set; }
-
- public bool Grant { get; set; }
-
- public bool WithGrant { get; set; }
-
- public bool Deny { get; set; }
- }
-
- public class SecurablePermissions
- {
- public SqlObject Securable { get; set; }
-
- public Permission[] Permissions { get; set; }
- }
///
/// a class for storing various user properties
@@ -60,69 +45,44 @@ namespace Microsoft.SqlTools.ServiceLayer.Security.Contracts
{
public DatabaseUserType? Type { get; set; }
- public string UserName { get; set; }
+ public string? Name { get; set; }
- public string LoginName { get; set; }
+ public string? LoginName { get; set; }
- public string Password { get; set; }
+ public string? Password { get; set; }
- public string DefaultSchema { get; set; }
+ public string? DefaultSchema { get; set; }
- public string[] OwnedSchemas { get; set; }
+ public string[]? OwnedSchemas { get; set; }
- public bool isEnabled { get; set; }
+ public string[]? DatabaseRoles { get; set; }
- public bool isAAD { get; set; }
+ public ServerAuthenticationType AuthenticationType { get; set; }
- public ExtendedProperty[]? ExtendedProperties { get; set; }
+ public string? DefaultLanguage { get; set; }
+ }
- public SecurablePermissions[]? SecurablePermissions { get; set; }
+ ///
+ /// The information required to render the user view.
+ ///
+ public class UserViewInfo
+ {
+ public UserInfo? ObjectInfo { get; set; }
+
+ public bool SupportContainedUser { get; set; }
+
+ public bool SupportWindowsAuthentication { get; set; }
+
+ public bool SupportAADAuthentication { get; set; }
+
+ public bool SupportSQLAuthentication { get; set; }
+
+ public string[]? Languages { get; set; }
+
+ public string[]? Schemas { get; set; }
+
+ public string[]? Logins { get; set; }
+
+ public string[]? DatabaseRoles { get; set; }
}
}
-
-
-#if false
-
-export interface ServerRole extends SqlObject {
- owner: string | undefined;
- securablePermissions: SecurablePermissions[];
- members: SqlObject[];
- memberships: SqlObject[];
- isFixedRole: boolean;
-}
-
-export interface ServerLogin extends SqlObject {
- type: LoginType;
- password: string | undefined;
- oldPassword: string | undefined;
- enforcePasswordPolicy: boolean | undefined;
- enforcePasswordExpiration: boolean | undefined;
- defaultDatabase: string;
- defaultLanguage: string;
- serverRoles: string[];
- userMapping: ServerLoginDatabaseUserMapping[];
- isGroup: boolean;
- isEnabled: boolean;
- connectPermission: boolean;
- isLockedOut: boolean;
-}
-
-
-
-export interface ServerLoginDatabaseUserMapping {
- database: string;
- user: string;
- defaultSchema: string;
- databaseRoles: string[];
-}
-
-export interface DatabaseRole extends SqlObject {
- owner: string | undefined;
- password: string | undefined;
- ownedSchemas: string[];
- securablePermissions: SecurablePermissions[] | undefined;
- extendedProperties: ExtendedProperty[] | undefined;
- isFixedRole: boolean;
-}
-
-#endif
\ No newline at end of file
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Security/Contracts/UserRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/Security/Contracts/UserRequest.cs
index 99332e72..45de2825 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Security/Contracts/UserRequest.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Security/Contracts/UserRequest.cs
@@ -8,15 +8,43 @@ using Microsoft.SqlTools.ServiceLayer.Utility;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Security.Contracts
-{
+{
+ ///
+ /// Initialize User View parameters
+ ///
+ public class InitializeUserViewParams
+ {
+ public string? ContextId { get; set; }
+
+ public string? ConnectionUri { get; set; }
+
+ public bool isNewObject { get; set; }
+
+ public string? Database { get; set; }
+
+ public string? Name { get; set; }
+ }
+
+ ///
+ /// Initialize User View request type
+ ///
+ public class InitializeUserViewRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("objectManagement/initializeUserView");
+ }
+
///
/// Create User parameters
///
public class CreateUserParams : GeneralRequestDetails
{
- public string OwnerUri { get; set; }
-
- public UserInfo User { get; set; }
+ public string? ContextId { get; set; }
+ public UserInfo? User { get; set; }
}
///
@@ -24,10 +52,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Security.Contracts
///
public class CreateUserResult : ResultStatus
{
- public UserInfo User { get; set; }
+ public UserInfo? User { get; set; }
}
-
///
/// Create User request type
///
@@ -38,17 +65,19 @@ namespace Microsoft.SqlTools.ServiceLayer.Security.Contracts
///
public static readonly
RequestType Type =
- RequestType.Create("objectmanagement/createuser");
+ RequestType.Create("objectManagement/createUser");
}
///
/// Delete User params
///
- public class DeleteUserParams : GeneralRequestDetails
+ public class DeleteUserParams
{
- public string OwnerUri { get; set; }
-
- public string UserName { get; set; }
+ public string? ConnectionUri { get; set; }
+
+ public string? Database { get; set; }
+
+ public string? Name { get; set; }
}
///
@@ -61,6 +90,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Security.Contracts
///
public static readonly
RequestType Type =
- RequestType.Create("objectmanagement/deleteuser");
+ RequestType.Create("objectManagement/deleteUser");
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Security/SecurityService.cs b/src/Microsoft.SqlTools.ServiceLayer/Security/SecurityService.cs
index b0bd7c33..a0cf554f 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Security/SecurityService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Security/SecurityService.cs
@@ -3,12 +3,11 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
-#nullable disable
-
using System;
using System.Collections;
-using System.Collections.Specialized;
+using System.Collections.Generic;
using System.Data;
+using System.Linq;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.SqlServer.Management.Common;
@@ -31,10 +30,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
{
private bool disposed;
- private ConnectionService connectionService;
+ private ConnectionService? connectionService;
private static readonly Lazy instance = new Lazy(() => new SecurityService());
+ private Dictionary contextIdToConnectionUriMap = new Dictionary();
+
///
/// Construct a new SecurityService instance with default parameters
///
@@ -71,7 +72,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
/// Service host object for sending/receiving requests/events.
/// Internal for testing purposes.
///
- internal IProtocolEndpoint ServiceHost
+ internal IProtocolEndpoint? ServiceHost
{
get;
set;
@@ -95,11 +96,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
this.ServiceHost.SetRequestHandler(DeleteLoginRequest.Type, HandleDeleteLoginRequest, true);
// User request handlers
+ this.ServiceHost.SetRequestHandler(InitializeUserViewRequest.Type, HandleInitializeUserViewRequest, true);
this.ServiceHost.SetRequestHandler(CreateUserRequest.Type, HandleCreateUserRequest, true);
+ this.ServiceHost.SetRequestHandler(DeleteUserRequest.Type, HandleDeleteUserRequest, true);
}
-#region "Login Handlers"
+ #region "Login Handlers"
///
/// Handle request to create a login
@@ -128,10 +131,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
}
// check that password and confirm password controls' text matches
- if (0 != String.Compare(prototype.SqlPassword, prototype.SqlPasswordConfirm, StringComparison.Ordinal))
+ if (0 != string.Compare(prototype.SqlPassword, prototype.SqlPasswordConfirm, StringComparison.Ordinal))
{
// raise error here
- }
+ }
}
prototype.ApplyGeneralChanges(dataContainer.Server);
@@ -157,11 +160,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
// }
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
- Login login = dataContainer.Server.Logins[parameters.LoginName];
-
+ Login login = dataContainer.Server?.Logins[parameters.LoginName];
+
dataContainer.SqlDialogSubject = login;
DoDropObject(dataContainer);
-
+
await requestContext.SendResult(new ResultStatus()
{
Success = true,
@@ -169,13 +172,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
});
}
-#endregion
+ #endregion
-#region "User Handlers"
+ #region "User Handlers"
internal Task> ConfigureUser(
- string ownerUri,
- UserInfo user,
+ string? ownerUri,
+ UserInfo? user,
ConfigAction configAction,
RunType runType)
{
@@ -194,8 +197,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
var connectionInfoWithConnection = new SqlConnectionInfoWithConnection();
connectionInfoWithConnection.ServerConnection = serverConnection;
- string urn = string.Format(System.Globalization.CultureInfo.InvariantCulture,
- "Server/Database[@Name='{0}']",
+ string urn = string.Format(System.Globalization.CultureInfo.InvariantCulture,
+ "Server/Database[@Name='{0}']",
Urn.EscapeString(serverConnection.DatabaseName));
ActionContext context = new ActionContext(serverConnection, "new_user", urn);
@@ -221,12 +224,142 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
});
}
+ private Dictionary LoadSchemas(string databaseName, string dbroleName, ServerConnection serverConnection)
+ {
+ bool isPropertiesMode = false;
+ Dictionary schemaOwnership = new Dictionary();
+
+ Enumerator en = new Enumerator();
+ Request req = new Request();
+ req.Fields = new String[] { "Name", "Owner" };
+ req.Urn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']/Schema";
+
+ DataTable dt = en.Process(serverConnection, req);
+ System.Diagnostics.Debug.Assert((dt != null) && (0 < dt.Rows.Count), "enumerator did not return schemas");
+ System.Diagnostics.Debug.Assert(!isPropertiesMode || (dbroleName.Length != 0), "role name is not known");
+
+ foreach (DataRow dr in dt.Rows)
+ {
+ string schemaName = Convert.ToString(dr["Name"], System.Globalization.CultureInfo.InvariantCulture);
+ string schemaOwner = Convert.ToString(dr["Owner"], System.Globalization.CultureInfo.InvariantCulture);
+ bool roleOwnsSchema = isPropertiesMode && (0 == String.Compare(dbroleName, schemaOwner, StringComparison.Ordinal));
+ if (schemaName != null)
+ {
+ schemaOwnership[schemaName] = new SchemaOwnership(roleOwnsSchema);
+ }
+ }
+ return schemaOwnership;
+ }
+
+ private string[] LoadDatabaseRoles(ServerConnection serverConnection, string databaseName)
+ {
+ var server = new Server(serverConnection);
+ string dbUrn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']";
+ Database? parent = server.GetSmoObject(new Urn(dbUrn)) as Database;
+ var roles = new List();
+ if (parent != null)
+ {
+ foreach (DatabaseRole dbRole in parent.Roles)
+ {
+ var comparer = parent.GetStringComparer();
+ if (comparer.Compare(dbRole.Name, "public") != 0)
+ {
+ roles.Add(dbRole.Name);
+ }
+ }
+ }
+ return roles.ToArray();
+ }
+
+ private string[] LoadSqlLogins(ServerConnection serverConnection)
+ {
+ return LoadItems(serverConnection, "Server/Login");
+ }
+
+ private string[] LoadItems(ServerConnection serverConnection, string urn)
+ {
+ List items = new List();
+ Request req = new Request();
+ req.Urn = urn;
+ req.ResultType = ResultType.IDataReader;
+ req.Fields = new string[] { "Name" };
+
+ Enumerator en = new Enumerator();
+ using (IDataReader reader = en.Process(serverConnection, req).Data as IDataReader)
+ {
+ if (reader != null)
+ {
+ string name;
+ while (reader.Read())
+ {
+ // Get the permission name
+ name = reader.GetString(0);
+ items.Add(name);
+ }
+ }
+ }
+ return items.ToArray();
+ }
+
+ ///
+ /// Handle request to initialize user view
+ ///
+ internal async Task HandleInitializeUserViewRequest(InitializeUserViewParams parameters, RequestContext requestContext)
+ {
+ ConnectionInfo connInfo;
+ ConnectionServiceInstance.TryFindConnection(parameters.ConnectionUri, out connInfo);
+ if (connInfo == null)
+ {
+ throw new ArgumentException("Invalid connection URI '{0}'", parameters.ConnectionUri);
+ }
+
+ if (parameters.ContextId != null && parameters.ConnectionUri != null)
+ {
+ this.contextIdToConnectionUriMap.Add(parameters.ContextId, parameters.ConnectionUri);
+ }
+
+ var serverConnection = ConnectionService.OpenServerConnection(connInfo, "DataContainer");
+
+ string databaseName = parameters.Database ?? "master";
+ var schemaMap = LoadSchemas(databaseName, string.Empty, serverConnection);
+
+ UserViewInfo userViewInfo = new UserViewInfo()
+ {
+ ObjectInfo = new UserInfo()
+ {
+ Type = DatabaseUserType.WithLogin,
+ Name = string.Empty,
+ LoginName = string.Empty,
+ Password = string.Empty,
+ DefaultSchema = string.Empty,
+ OwnedSchemas = new string[] { },
+ DatabaseRoles = new string[] { },
+ },
+ SupportContainedUser = true,
+ SupportWindowsAuthentication = true,
+ SupportAADAuthentication = true,
+ SupportSQLAuthentication = true,
+ Languages = new string[] { },
+ Schemas = schemaMap.Keys.ToArray(),
+ Logins = LoadSqlLogins(serverConnection),
+ DatabaseRoles = LoadDatabaseRoles(serverConnection, databaseName)
+ };
+
+ await requestContext.SendResult(userViewInfo);
+ }
+
///
/// Handle request to create a user
///
internal async Task HandleCreateUserRequest(CreateUserParams parameters, RequestContext requestContext)
{
- var result = await ConfigureUser(parameters.OwnerUri,
+ if (parameters.ContextId == null || !this.contextIdToConnectionUriMap.ContainsKey(parameters.ContextId))
+ {
+ throw new ArgumentException("Invalid context ID");
+ }
+
+ string connectionUri = this.contextIdToConnectionUriMap[parameters.ContextId];
+ var result = await ConfigureUser(connectionUri,
parameters.User,
ConfigAction.Create,
RunType.RunNow);
@@ -236,10 +369,35 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
User = parameters.User,
Success = result.Item1,
ErrorMessage = result.Item2
- });
+ });
+ }
+
+ ///
+ /// Handle request to delete a user
+ ///
+ internal async Task HandleDeleteUserRequest(DeleteUserParams parameters, RequestContext requestContext)
+ {
+ ConnectionInfo connInfo;
+ ConnectionServiceInstance.TryFindConnection(parameters.ConnectionUri, out connInfo);
+ // if (connInfo == null)
+ // {
+ // // raise an error
+ // }
+
+ CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
+ string dbUrn = "Server/Database[@Name='" + Urn.EscapeString(parameters.Database) + "']";
+ Database? parent = dataContainer.Server.GetSmoObject(new Urn(dbUrn)) as Database;
+ User user = parent.Users[parameters.Name];
+ dataContainer.SqlDialogSubject = user;
+ DoDropObject(dataContainer);
+
+ await requestContext.SendResult(new ResultStatus()
+ {
+ Success = true,
+ ErrorMessage = string.Empty
+ });
}
-
private void GetDefaultLanguageOptions(CDataContainer dataContainer)
{
// this.defaultLanguageComboBox.Items.Clear();
@@ -249,10 +407,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
SortedList sortedLanguages = new SortedList(Comparer.Default);
LanguageUtils.SetLanguageDefaultInitFieldsForDefaultLanguages(dataContainer.Server);
- foreach (Language language in dataContainer.Server.Languages)
+ if (dataContainer.Server != null && dataContainer.Server.Languages != null)
{
- LanguageDisplay listValue = new LanguageDisplay(language);
- sortedLanguages.Add(language.Alias, listValue);
+ foreach (Language language in dataContainer.Server.Languages)
+ {
+ LanguageDisplay listValue = new LanguageDisplay(language);
+ sortedLanguages.Add(language.Alias, listValue);
+ }
}
// add the language display objects to the combo box
@@ -314,22 +475,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
// how to populate defaults from prototype, will delete once refactored
// private void InitializeValuesInUiControls()
// {
- // this.userNameTextBox.Text = this.currentUserPrototype.Name;
-
- // if(this.currentUserPrototype.UserType == UserType.Certificate)
- // {
- // this.mappedObjTextbox.Text = this.currentUserPrototype.CertificateName;
- // }
- // if (this.currentUserPrototype.UserType == UserType.AsymmetricKey)
- // {
- // this.mappedObjTextbox.Text = this.currentUserPrototype.AsymmetricKeyName;
- // }
- // IUserPrototypeWithMappedLogin mappedLoginPrototype = this.currentUserPrototype
- // as IUserPrototypeWithMappedLogin;
- // if (mappedLoginPrototype != null)
- // {
- // this.mappedObjTextbox.Text = mappedLoginPrototype.LoginName;
- // }
// IUserPrototypeWithDefaultLanguage defaultLanguagePrototype = this.currentUserPrototype
// as IUserPrototypeWithDefaultLanguage;
@@ -356,7 +501,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
// {
// this.defaultSchemaTextBox.Text = defaultSchemaPrototype.DefaultSchema;
// }
-
// IUserPrototypeWithPassword userWithPwdPrototype = this.currentUserPrototype
// as IUserPrototypeWithPassword;
// if (userWithPwdPrototype != null
@@ -366,31 +510,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
// this.confirmPwdTextBox.Text = FAKE_PASSWORD;
// }
// }
- // private void UpdateUiControlsOnLoad()
- // {
- // if (!this.DataContainer.IsNewObject)
- // {
- // this.userNameTextBox.ReadOnly = true; //Rename is not allowed from the dialog.
- // this.userSearchButton.Enabled = false;
- // this.mappedObjTextbox.ReadOnly = true; //Changing mapped login, certificate and asymmetric key is not allowed
- // this.mappedObjSearchButton.Enabled = false;
- // //from SMO also.
- // this.userTypeComboBox.Enabled = false;
- // this.oldPasswordTextBox.ReadOnly = true;
- // }
- // else
- // {
- // //Old password is only useful for changing the password.
- // this.specifyOldPwdCheckBox.Enabled = false;
- // this.oldPasswordLabel.Enabled = false;
- // this.oldPasswordTextBox.Enabled = false;
- // }
- // }
-#endregion
-#region "Credential Handlers"
+
+ #endregion
+
+ #region "Credential Handlers"
///
/// Handle request to create a credential
@@ -458,18 +584,21 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
ConnectionServiceInstance.TryFindConnection(parameters.OwnerUri, out connInfo);
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
- var credentials = dataContainer.Server.Credentials;
- int credentialsCount = credentials.Count;
+ var credentials = dataContainer.Server?.Credentials;
+ int credentialsCount = credentials != null ? credentials.Count : 0;
CredentialInfo[] credentialsInfos = new CredentialInfo[credentialsCount];
- for (int i = 0; i < credentialsCount; ++i)
+ if (credentials != null)
{
- credentialsInfos[i] = new CredentialInfo();
- credentialsInfos[i].Name = credentials[i].Name;
- credentialsInfos[i].Identity = credentials[i].Identity;
- credentialsInfos[i].Id = credentials[i].ID;
- credentialsInfos[i].DateLastModified = credentials[i].DateLastModified;
- credentialsInfos[i].CreateDate = credentials[i].CreateDate;
- credentialsInfos[i].ProviderName = credentials[i].ProviderName;
+ for (int i = 0; i < credentialsCount; ++i)
+ {
+ credentialsInfos[i] = new CredentialInfo();
+ credentialsInfos[i].Name = credentials[i].Name;
+ credentialsInfos[i].Identity = credentials[i].Identity;
+ credentialsInfos[i].Id = credentials[i].ID;
+ credentialsInfos[i].DateLastModified = credentials[i].DateLastModified;
+ credentialsInfos[i].CreateDate = credentials[i].CreateDate;
+ credentialsInfos[i].ProviderName = credentials[i].ProviderName;
+ }
}
result.Credentials = credentialsInfos;
result.Success = true;
@@ -494,9 +623,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
}
}
-#endregion
+ #endregion
-#region "Helpers"
+ #region "Helpers"
internal Task> ConfigureCredential(
string ownerUri,
@@ -533,7 +662,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
///
///
private void DoDropObject(CDataContainer dataContainer)
- {
+ {
+ // if a server isn't connected then there is nothing to do
+ if (dataContainer.Server == null)
+ {
+ return;
+ }
+
var executionMode = dataContainer.Server.ConnectionContext.SqlExecutionModes;
var subjectExecutionMode = executionMode;
@@ -557,15 +692,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
sqlDialogSubject.ExecutionManager.ConnectionContext.SqlExecutionModes;
}
- Urn objUrn = sqlDialogSubject.Urn;
+ Urn objUrn = sqlDialogSubject?.Urn;
System.Diagnostics.Debug.Assert(objUrn != null);
SfcObjectQuery objectQuery = new SfcObjectQuery(dataContainer.Server);
-
+
IDroppable droppableObj = null;
string[] fields = null;
- foreach( object obj in objectQuery.ExecuteIterator( new SfcQueryExpression( objUrn.ToString() ), fields, null ) )
+ foreach (object obj in objectQuery.ExecuteIterator(new SfcQueryExpression(objUrn.ToString()), fields, null))
{
System.Diagnostics.Debug.Assert(droppableObj == null, "there is only one object");
droppableObj = obj as IDroppable;
@@ -586,7 +721,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
}
//special case database drop - see if we need to delete backup and restore history
- SpecialPreDropActionsForObject(dataContainer, droppableObj,
+ SpecialPreDropActionsForObject(dataContainer, droppableObj,
deleteBackupRestoreOrDisableAuditSpecOrDisableAudit: false,
dropOpenConnections: false);
@@ -596,10 +731,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
SpecialPostDropActionsForObject(dataContainer, droppableObj);
}
-
- private void SpecialPreDropActionsForObject(CDataContainer dataContainer, IDroppable droppableObj,
+
+ private void SpecialPreDropActionsForObject(CDataContainer dataContainer, IDroppable droppableObj,
bool deleteBackupRestoreOrDisableAuditSpecOrDisableAudit, bool dropOpenConnections)
{
+ // if a server isn't connected then there is nothing to do
+ if (dataContainer.Server == null)
+ {
+ return;
+ }
+
Database db = droppableObj as Database;
if (deleteBackupRestoreOrDisableAuditSpecOrDisableAudit)
@@ -638,7 +779,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
// special case database drop - drop existing connections to the database other than this one
if (dropOpenConnections)
{
- if (db.ActiveConnections > 0)
+ if (db?.ActiveConnections > 0)
{
// force the database to be single user
db.DatabaseOptions.UserAccess = DatabaseUserAccess.Single;
@@ -649,6 +790,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
private void SpecialPostDropActionsForObject(CDataContainer dataContainer, IDroppable droppableObj)
{
+ // if a server isn't connected then there is nothing to do
+ if (dataContainer.Server == null)
+ {
+ return;
+ }
+
if (droppableObj is Policy)
{
Policy policyToDrop = (Policy)droppableObj;
@@ -670,10 +817,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
}
}
-#endregion // "Helpers"
+ #endregion // "Helpers"
-// some potentially useful code for working with server & db roles to be refactored later
-#region "Roles"
+ // some potentially useful code for working with server & db roles to be refactored later
+ #region "Roles"
private class SchemaOwnership
{
public bool initiallyOwned;
@@ -704,199 +851,181 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
}
}
- private void DbRole_LoadMembership(string databaseName, string dbroleName, ServerConnection serverConnection)
- {
- var roleMembers = new HybridDictionary();
- bool isPropertiesMode = false;
- if (isPropertiesMode)
- {
- Enumerator enumerator = new Enumerator();
- Urn urn = String.Format(System.Globalization.CultureInfo.InvariantCulture,
- "Server/Database[@Name='{0}']/Role[@Name='{1}']/Member",
- Urn.EscapeString(databaseName),
- Urn.EscapeString(dbroleName));
- string[] fields = new string[] { "Name" };
- OrderBy[] orderBy = new OrderBy[] { new OrderBy("Name", OrderBy.Direction.Asc)};
- Request request = new Request(urn, fields, orderBy);
- DataTable dt = enumerator.Process(serverConnection, request);
+ // private void DbRole_LoadMembership(string databaseName, string dbroleName, ServerConnection serverConnection)
+ // {
+ // var roleMembers = new HybridDictionary();
+ // bool isPropertiesMode = false;
+ // if (isPropertiesMode)
+ // {
+ // Enumerator enumerator = new Enumerator();
+ // Urn urn = String.Format(System.Globalization.CultureInfo.InvariantCulture,
+ // "Server/Database[@Name='{0}']/Role[@Name='{1}']/Member",
+ // Urn.EscapeString(databaseName),
+ // Urn.EscapeString(dbroleName));
+ // string[] fields = new string[] { "Name" };
+ // OrderBy[] orderBy = new OrderBy[] { new OrderBy("Name", OrderBy.Direction.Asc)};
+ // Request request = new Request(urn, fields, orderBy);
+ // DataTable dt = enumerator.Process(serverConnection, request);
- foreach (DataRow dr in dt.Rows)
- {
- string memberName = dr["Name"].ToString();
- roleMembers[memberName] = new RoleMembership(true);
- }
- }
- }
+ // foreach (DataRow dr in dt.Rows)
+ // {
+ // string memberName = dr["Name"].ToString();
+ // if (memberName != null)
+ // {
+ // roleMembers[memberName] = new RoleMembership(true);
+ // }
+ // }
+ // }
+ // }
- ///
- /// sends to server user changes related to membership
- ///
- private void DbRole_SendToServerMembershipChanges(Database db, DatabaseRole dbrole)
- {
- var roleMembers = new HybridDictionary();
- IDictionaryEnumerator enumerator = roleMembers.GetEnumerator();
- enumerator.Reset();
+ // ///
+ // /// sends to server user changes related to membership
+ // ///
+ // private void DbRole_SendToServerMembershipChanges(Database db, DatabaseRole dbrole)
+ // {
+ // var roleMembers = new HybridDictionary();
+ // IDictionaryEnumerator enumerator = roleMembers.GetEnumerator();
+ // enumerator.Reset();
- while (enumerator.MoveNext())
- {
- DictionaryEntry entry = enumerator.Entry;
- string memberName = entry.Key.ToString();
- RoleMembership membership = (RoleMembership) entry.Value;
+ // while (enumerator.MoveNext())
+ // {
+ // DictionaryEntry entry = enumerator.Entry;
+ // string memberName = entry.Key.ToString();
+ // RoleMembership membership = (RoleMembership) entry.Value;
+ // if (membership != null)
+ // {
+ // if (!membership.initiallyAMember && membership.currentlyAMember)
+ // {
+ // dbrole.AddMember(memberName);
+ // }
+ // else if (membership.initiallyAMember && !membership.currentlyAMember)
+ // {
+ // dbrole.DropMember(memberName);
+ // }
+ // }
+ // }
+ // }
- if (!membership.initiallyAMember && membership.currentlyAMember)
- {
- dbrole.AddMember(memberName);
- }
- else if (membership.initiallyAMember && !membership.currentlyAMember)
- {
- dbrole.DropMember(memberName);
- }
- }
- }
+ // private void InitProp(ServerConnection serverConnection, string serverName, string databaseName,
+ // string dbroleName, string dbroleUrn, bool isPropertiesMode)
+ // {
+ // System.Diagnostics.Debug.Assert(serverName!=null);
+ // System.Diagnostics.Debug.Assert((databaseName!=null) && (databaseName.Trim().Length!=0));
- private void InitProp(ServerConnection serverConnection, string serverName, string databaseName,
- string dbroleName, string dbroleUrn, bool isPropertiesMode)
- {
- System.Diagnostics.Debug.Assert(serverName!=null);
- System.Diagnostics.Debug.Assert((databaseName!=null) && (databaseName.Trim().Length!=0));
+ // // LoadSchemas();
+ // // LoadMembership();
- // LoadSchemas();
- // LoadMembership();
+ // if (isPropertiesMode == true)
+ // {
+ // // initialize from enumerator in properties mode
+ // System.Diagnostics.Debug.Assert(dbroleName!=null);
+ // System.Diagnostics.Debug.Assert(dbroleName.Trim().Length !=0);
+ // System.Diagnostics.Debug.Assert(dbroleUrn!=null);
+ // System.Diagnostics.Debug.Assert(dbroleUrn.Trim().Length != 0);
- if (isPropertiesMode == true)
- {
- // initialize from enumerator in properties mode
- System.Diagnostics.Debug.Assert(dbroleName!=null);
- System.Diagnostics.Debug.Assert(dbroleName.Trim().Length !=0);
- System.Diagnostics.Debug.Assert(dbroleUrn!=null);
- System.Diagnostics.Debug.Assert(dbroleUrn.Trim().Length != 0);
+ // Enumerator en = new Enumerator();
+ // Request req = new Request();
+ // req.Fields = new String [] { "Owner" };
- Enumerator en = new Enumerator();
- Request req = new Request();
- req.Fields = new String [] { "Owner" };
+ // if ((dbroleUrn!=null) && (dbroleUrn.Trim().Length != 0))
+ // {
+ // req.Urn = dbroleUrn;
+ // }
+ // else
+ // {
+ // req.Urn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']/Role[@Name='" + Urn.EscapeString(dbroleName) + "]";
+ // }
- if ((dbroleUrn!=null) && (dbroleUrn.Trim().Length != 0))
- {
- req.Urn = dbroleUrn;
- }
- else
- {
- req.Urn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']/Role[@Name='" + Urn.EscapeString(dbroleName) + "]";
- }
+ // DataTable dt = en.Process(serverConnection, req);
+ // System.Diagnostics.Debug.Assert(dt!=null);
+ // System.Diagnostics.Debug.Assert(dt.Rows.Count==1);
- DataTable dt = en.Process(serverConnection, req);
- System.Diagnostics.Debug.Assert(dt!=null);
- System.Diagnostics.Debug.Assert(dt.Rows.Count==1);
+ // if (dt.Rows.Count==0)
+ // {
+ // throw new Exception("DatabaseRoleSR.ErrorDbRoleNotFound");
+ // }
- if (dt.Rows.Count==0)
- {
- throw new Exception("DatabaseRoleSR.ErrorDbRoleNotFound");
- }
+ // // DataRow dr = dt.Rows[0];
+ // // this.initialOwner = Convert.ToString(dr[DatabaseRoleGeneral.ownerField],System.Globalization.CultureInfo.InvariantCulture);
+ // // this.textBoxOwner.Text = this.initialOwner;
+ // }
+ // }
- // DataRow dr = dt.Rows[0];
- // this.initialOwner = Convert.ToString(dr[DatabaseRoleGeneral.ownerField],System.Globalization.CultureInfo.InvariantCulture);
- // this.textBoxOwner.Text = this.initialOwner;
- }
- }
+ // private void DbRole_SendDataToServer(CDataContainer dataContainer, string databaseName,
+ // string dbroleName, string ownerName, string initialOwner, string roleName, bool isPropertiesMode)
+ // {
+ // System.Diagnostics.Debug.Assert(databaseName != null && databaseName.Trim().Length != 0, "database name is empty");
+ // System.Diagnostics.Debug.Assert(dataContainer.Server != null, "server is null");
- private void DbRole_SendDataToServer(CDataContainer dataContainer, string databaseName,
- string dbroleName, string ownerName, string initialOwner, string roleName, bool isPropertiesMode)
- {
- System.Diagnostics.Debug.Assert(databaseName != null && databaseName.Trim().Length != 0, "database name is empty");
- System.Diagnostics.Debug.Assert(dataContainer.Server != null, "server is null");
+ // Database database = dataContainer.Server.Databases[databaseName];
+ // System.Diagnostics.Debug.Assert(database!= null, "database is null");
- Database database = dataContainer.Server.Databases[databaseName];
- System.Diagnostics.Debug.Assert(database!= null, "database is null");
+ // DatabaseRole role;
- DatabaseRole role;
+ // if (isPropertiesMode == true) // in properties mode -> alter role
+ // {
+ // System.Diagnostics.Debug.Assert(dbroleName != null && dbroleName.Trim().Length != 0, "role name is empty");
- if (isPropertiesMode == true) // in properties mode -> alter role
- {
- System.Diagnostics.Debug.Assert(dbroleName != null && dbroleName.Trim().Length != 0, "role name is empty");
+ // role = database.Roles[dbroleName];
+ // System.Diagnostics.Debug.Assert(role != null, "role is null");
- role = database.Roles[dbroleName];
- System.Diagnostics.Debug.Assert(role != null, "role is null");
+ // if (0 != String.Compare(ownerName, initialOwner, StringComparison.Ordinal))
+ // {
+ // role.Owner = ownerName;
+ // role.Alter();
+ // }
+ // }
+ // else // not in properties mode -> create role
+ // {
+ // role = new DatabaseRole(database, roleName);
+ // if (ownerName.Length != 0)
+ // {
+ // role.Owner = ownerName;
+ // }
- if (0 != String.Compare(ownerName, initialOwner, StringComparison.Ordinal))
- {
- role.Owner = ownerName;
- role.Alter();
- }
- }
- else // not in properties mode -> create role
- {
- role = new DatabaseRole(database, roleName);
- if (ownerName.Length != 0)
- {
- role.Owner = ownerName;
- }
+ // role.Create();
+ // }
- role.Create();
- }
+ // // SendToServerSchemaOwnershipChanges(database, role);
+ // // SendToServerMembershipChanges(database, role);
+ // }
- // SendToServerSchemaOwnershipChanges(database, role);
- // SendToServerMembershipChanges(database, role);
- }
- private void DbRole_LoadSchemas(string databaseName, string dbroleName, ServerConnection serverConnection)
- {
- bool isPropertiesMode = false;
- HybridDictionary schemaOwnership;
- schemaOwnership = new HybridDictionary();
+ // ///
+ // /// sends to server changes related to schema ownership
+ // ///
+ // private void DbRole_SendToServerSchemaOwnershipChanges(CDataContainer dataContainer, Database db, DatabaseRole dbrole)
+ // {
+ // if (dataContainer.Server == null)
+ // {
+ // return;
+ // }
- Enumerator en = new Enumerator();
- Request req = new Request();
- req.Fields = new String [] { "Name", "Owner" };
- req.Urn = "Server/Database[@Name='" + Urn.EscapeString(databaseName) + "']/Schema";
+ // HybridDictionary schemaOwnership = new HybridDictionary();
+ // if (9 <= dataContainer.Server.Information.Version.Major)
+ // {
+ // IDictionaryEnumerator enumerator = schemaOwnership.GetEnumerator();
+ // enumerator.Reset();
+ // while (enumerator.MoveNext())
+ // {
+ // DictionaryEntry de = enumerator.Entry;
+ // string schemaName = de.Key.ToString();
+ // SchemaOwnership ownership = (SchemaOwnership)de.Value;
- DataTable dt = en.Process(serverConnection, req);
- System.Diagnostics.Debug.Assert((dt != null) && (0 < dt.Rows.Count), "enumerator did not return schemas");
- System.Diagnostics.Debug.Assert(!isPropertiesMode || (dbroleName.Length != 0), "role name is not known");
+ // // If we are creating a new role, then no schema will have been initially owned by this role.
+ // // If we are modifying an existing role, we can only take ownership of roles. (Ownership can't
+ // // be renounced, it can only be positively assigned to a principal.)
+ // if (ownership != null && (ownership.currentlyOwned && !ownership.initiallyOwned))
+ // {
+ // Schema schema = db.Schemas[schemaName];
+ // schema.Owner = dbrole.Name;
+ // schema.Alter();
+ // }
+ // }
+ // }
+ // }
- foreach (DataRow dr in dt.Rows)
- {
- string schemaName = Convert.ToString(dr["Name"],System.Globalization.CultureInfo.InvariantCulture);
- string schemaOwner = Convert.ToString(dr["Owner"],System.Globalization.CultureInfo.InvariantCulture);
- bool roleOwnsSchema =
- isPropertiesMode &&
- (0 == String.Compare(dbroleName, schemaOwner, StringComparison.Ordinal));
-
- schemaOwnership[schemaName] = new SchemaOwnership(roleOwnsSchema);
- }
- }
-
- ///
- /// sends to server changes related to schema ownership
- ///
- private void DbRole_SendToServerSchemaOwnershipChanges(CDataContainer dataContainer, Database db, DatabaseRole dbrole)
- {
- HybridDictionary schemaOwnership = null;
- if (9 <= dataContainer.Server.Information.Version.Major)
- {
- IDictionaryEnumerator enumerator = schemaOwnership.GetEnumerator();
- enumerator.Reset();
- while (enumerator.MoveNext())
- {
- DictionaryEntry de = enumerator.Entry;
- string schemaName = de.Key.ToString();
- SchemaOwnership ownership = (SchemaOwnership)de.Value;
-
- // If we are creating a new role, then no schema will have been initially owned by this role.
- // If we are modifying an existing role, we can only take ownership of roles. (Ownership can't
- // be renounced, it can only be positively assigned to a principal.)
- if (ownership.currentlyOwned && !ownership.initiallyOwned)
- {
- Schema schema = db.Schemas[schemaName];
- schema.Owner = dbrole.Name;
- schema.Alter();
- }
- }
- }
- }
-
-#endregion
+ #endregion
}
-
-
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Security/UserActions.cs b/src/Microsoft.SqlTools.ServiceLayer/Security/UserActions.cs
index 8907fade..acee754b 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Security/UserActions.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Security/UserActions.cs
@@ -26,7 +26,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
///
public UserActions(
CDataContainer context,
- UserInfo user,
+ UserInfo? user,
ConfigAction configAction)
{
this.DataContainer = context;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Security/UserData.cs b/src/Microsoft.SqlTools.ServiceLayer/Security/UserData.cs
index 2d61ca0f..28066a4c 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Security/UserData.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Security/UserData.cs
@@ -114,7 +114,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
}
else
{
- this.name = userInfo.UserName;
+ this.name = userInfo.Name;
this.mappedLoginName = userInfo.LoginName;
this.defaultSchemaName = userInfo.DefaultSchema;
this.password = DatabaseUtils.GetReadOnlySecureString(userInfo.Password);
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Utility/LanguageUtils.cs b/src/Microsoft.SqlTools.ServiceLayer/Utility/LanguageUtils.cs
index 8560c1fc..44f970cf 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Utility/LanguageUtils.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Utility/LanguageUtils.cs
@@ -150,7 +150,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
/// database and user dialogs as default init fields so that queries are not sent again and again.
///
/// server on which languages will be enumerated
- public static void SetLanguageDefaultInitFieldsForDefaultLanguages(Server connectedServer)
+ public static void SetLanguageDefaultInitFieldsForDefaultLanguages(Server? connectedServer)
{
string[] fieldsNeeded = new string[] { "Alias", "Name", "LocaleID", "LangID" };
connectedServer.SetDefaultInitFields(typeof(Language), fieldsNeeded);
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Security/SecurityTestUtils.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Security/SecurityTestUtils.cs
index ce7c6ef1..53cbf065 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Security/SecurityTestUtils.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Security/SecurityTestUtils.cs
@@ -51,16 +51,12 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Security
{
return new UserInfo()
{
- Type = DatabaseUserType.UserWithLogin,
- UserName = "TestUserName_" + new Random().NextInt64(10000000,90000000).ToString(),
+ Type = DatabaseUserType.WithLogin,
+ Name = "TestUserName_" + new Random().NextInt64(10000000,90000000).ToString(),
LoginName = loginName,
Password = "placeholder",
DefaultSchema = "dbo",
- OwnedSchemas = new string[] { "dbo" },
- isEnabled = false,
- isAAD = false,
- ExtendedProperties = null,
- SecurablePermissions = null
+ OwnedSchemas = new string[] { "dbo" }
};
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Security/UserTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Security/UserTests.cs
index 2ec90be0..33888698 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Security/UserTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Security/UserTests.cs
@@ -49,7 +49,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Security
var userParams = new CreateUserParams
{
- OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
+ ContextId = connectionResult.ConnectionInfo.OwnerUri,
User = SecurityTestUtils.GetTestUserInfo(loginParams.Login.LoginName)
};
@@ -62,7 +62,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Security
// verify the result
createUserContext.Verify(x => x.SendResult(It.Is
- (p => p.Success && p.User.UserName != string.Empty)));
+ (p => p.Success && p.User.Name != string.Empty)));
}
}
}