Fix nullable warnings in UserData class (#1843)

* Fix nullable warnings in UserData class

* Missing commit
This commit is contained in:
Karl Burtram
2023-02-03 09:51:31 -08:00
committed by GitHub
parent 2e950f54bd
commit 735517a503
3 changed files with 138 additions and 104 deletions

View File

@@ -168,7 +168,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
#region "User Handlers" #region "User Handlers"
private UserPrototypeNew InitUserNew(CDataContainer dataContainer) private UserPrototype InitUserNew(CDataContainer dataContainer)
{ {
// this.DataContainer = context; // this.DataContainer = context;
// this.parentDbUrn = new Urn(this.DataContainer.ParentUrn); // this.parentDbUrn = new Urn(this.DataContainer.ParentUrn);
@@ -193,7 +193,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
dataContainer.Server.GetSmoObject(dataContainer.ObjectUrn) as User); dataContainer.Server.GetSmoObject(dataContainer.ObjectUrn) as User);
} }
UserPrototypeNew currentUserPrototype = userPrototypeFactory.GetUserPrototype(currentUserType); UserPrototype currentUserPrototype = userPrototypeFactory.GetUserPrototype(currentUserType);
return currentUserPrototype; return currentUserPrototype;
} }
@@ -310,7 +310,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
public void UserMemberships_OnRunNow(object sender, CDataContainer dataContainer) public void UserMemberships_OnRunNow(object sender, CDataContainer dataContainer)
{ {
UserPrototypeNew currentPrototype = UserPrototypeFactory.GetInstance(dataContainer).CurrentPrototype; UserPrototype currentPrototype = UserPrototypeFactory.GetInstance(dataContainer).CurrentPrototype;
//In case the UserGeneral/OwnedSchemas pages are loaded, //In case the UserGeneral/OwnedSchemas pages are loaded,
//those will takes care of applying membership changes also. //those will takes care of applying membership changes also.
@@ -336,7 +336,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
/// <param name="node"></param> /// <param name="node"></param>
public void UserOwnedSchemas_OnRunNow(object sender, CDataContainer dataContainer) public void UserOwnedSchemas_OnRunNow(object sender, CDataContainer dataContainer)
{ {
UserPrototypeNew currentPrototype = UserPrototypeFactory.GetInstance(dataContainer).CurrentPrototype; UserPrototype currentPrototype = UserPrototypeFactory.GetInstance(dataContainer).CurrentPrototype;
//In case the UserGeneral/Membership pages are loaded, //In case the UserGeneral/Membership pages are loaded,
//those will takes care of applying schema ownership changes also. //those will takes care of applying schema ownership changes also.

View File

@@ -9,6 +9,7 @@ using System.Security;
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 System.Linq;
namespace Microsoft.SqlTools.ServiceLayer.Security namespace Microsoft.SqlTools.ServiceLayer.Security
{ {
@@ -72,13 +73,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
/// Object have exhaustive list of data elements which are required for creating /// Object have exhaustive list of data elements which are required for creating
/// any type of database user. /// any type of database user.
/// </summary> /// </summary>
public class UserPrototypeDataNew public class UserPrototypeData
{ {
public string name = string.Empty; public string name = string.Empty;
public UserType userType = UserType.SqlUser; public UserType userType = UserType.SqlUser;
public bool isSystemObject = false; public bool isSystemObject = false;
public Dictionary<string, bool> isSchemaOwned = null; public Dictionary<string, bool> isSchemaOwned;
public Dictionary<string, bool> isMember = null; public Dictionary<string, bool> isMember;
public AuthenticationType authenticationType = AuthenticationType.Instance; public AuthenticationType authenticationType = AuthenticationType.Instance;
public string mappedLoginName = string.Empty; public string mappedLoginName = string.Empty;
@@ -94,13 +95,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
/// <summary> /// <summary>
/// Used for creating clone of a UserPrototypeData. /// Used for creating clone of a UserPrototypeData.
/// </summary> /// </summary>
private UserPrototypeDataNew() private UserPrototypeData()
{ {
this.isSchemaOwned = new Dictionary<string, bool>(); this.isSchemaOwned = new Dictionary<string, bool>();
this.isMember = new Dictionary<string, bool>(); this.isMember = new Dictionary<string, bool>();
} }
public UserPrototypeDataNew(CDataContainer context) public UserPrototypeData(CDataContainer context)
{ {
this.isSchemaOwned = new Dictionary<string, bool>(); this.isSchemaOwned = new Dictionary<string, bool>();
this.isMember = new Dictionary<string, bool>(); this.isMember = new Dictionary<string, bool>();
@@ -115,9 +116,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
this.LoadSchemaData(context); this.LoadSchemaData(context);
} }
public UserPrototypeDataNew Clone() public UserPrototypeData Clone()
{ {
UserPrototypeDataNew result = new UserPrototypeDataNew(); UserPrototypeData result = new UserPrototypeData();
result.asymmetricKeyName = this.asymmetricKeyName; result.asymmetricKeyName = this.asymmetricKeyName;
result.authenticationType = this.authenticationType; result.authenticationType = this.authenticationType;
@@ -133,20 +134,28 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
result.isOldPasswordRequired = this.isOldPasswordRequired; result.isOldPasswordRequired = this.isOldPasswordRequired;
result.userType = this.userType; result.userType = this.userType;
foreach (string key in this.isMember.Keys) foreach (string key in this.isMember?.Keys ?? Enumerable.Empty<string>())
{ {
result.isMember[key] = this.isMember[key]; if (result.isMember?.ContainsKey(key) == true
&& this.isMember?.ContainsKey(key) == true)
{
result.isMember[key] = this.isMember[key];
}
} }
foreach (string key in this.isSchemaOwned.Keys) foreach (string key in this.isSchemaOwned?.Keys ?? Enumerable.Empty<string>())
{ {
result.isSchemaOwned[key] = this.isSchemaOwned[key]; if (result.isSchemaOwned?.ContainsKey(key) == true
&& this.isSchemaOwned?.ContainsKey(key) == true)
{
result.isSchemaOwned[key] = this.isSchemaOwned[key];
}
} }
return result; return result;
} }
public bool HasSameValueAs(UserPrototypeDataNew other) public bool HasSameValueAs(UserPrototypeData other)
{ {
bool result = bool result =
(this.asymmetricKeyName == other.asymmetricKeyName) && (this.asymmetricKeyName == other.asymmetricKeyName) &&
@@ -163,12 +172,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
(this.isOldPasswordRequired == other.isOldPasswordRequired) && (this.isOldPasswordRequired == other.isOldPasswordRequired) &&
(this.userType == other.userType); (this.userType == other.userType);
result = result && this.isMember.Keys.Count == other.isMember.Keys.Count; result = result && this.isMember?.Keys.Count == other.isMember?.Keys.Count;
if (result) if (result)
{ {
foreach (string key in this.isMember.Keys) foreach (string key in this.isMember?.Keys ?? Enumerable.Empty<string>())
{ {
if (this.isMember[key] != other.isMember[key]) if (this.isMember?.ContainsKey(key) == true
&& other.isMember?.ContainsKey(key) == true
&& this.isMember[key] != other.isMember[key])
{ {
result = false; result = false;
break; break;
@@ -198,7 +209,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
/// <param name="context"></param> /// <param name="context"></param>
private void LoadUserData(CDataContainer context) private void LoadUserData(CDataContainer context)
{ {
User existingUser = context.Server.GetSmoObject(new Urn(context.ObjectUrn)) as User; User? existingUser = context.Server.GetSmoObject(new Urn(context.ObjectUrn)) as User;
if (existingUser == null)
{
return;
}
this.name = existingUser.Name; this.name = existingUser.Name;
this.mappedLoginName = existingUser.Login; this.mappedLoginName = existingUser.Login;
@@ -229,7 +244,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
Urn objUrn = new Urn(context.ObjectUrn); Urn objUrn = new Urn(context.ObjectUrn);
Urn databaseUrn = objUrn.Parent; Urn databaseUrn = objUrn.Parent;
Database parentDb = context.Server.GetSmoObject(databaseUrn) as Database; Database? parentDb = context.Server.GetSmoObject(databaseUrn) as Database;
if (parentDb == null)
{
return;
}
User existingUser = context.Server.Databases[parentDb.Name].Users[objUrn.GetNameForType("User")]; User existingUser = context.Server.Databases[parentDb.Name].Users[objUrn.GetNameForType("User")];
foreach (DatabaseRole dbRole in parentDb.Roles) foreach (DatabaseRole dbRole in parentDb.Roles)
@@ -258,7 +278,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
Urn objUrn = new Urn(context.ObjectUrn); Urn objUrn = new Urn(context.ObjectUrn);
Urn databaseUrn = objUrn.Parent; Urn databaseUrn = objUrn.Parent;
Database parentDb = context.Server.GetSmoObject(databaseUrn) as Database; Database? parentDb = context.Server.GetSmoObject(databaseUrn) as Database;
if (parentDb == null)
{
return;
}
User existingUser = context.Server.Databases[parentDb.Name].Users[objUrn.GetNameForType("User")]; User existingUser = context.Server.Databases[parentDb.Name].Users[objUrn.GetNameForType("User")];
if (!SqlMgmtUtils.IsYukonOrAbove(context.Server) if (!SqlMgmtUtils.IsYukonOrAbove(context.Server)
@@ -285,15 +310,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
/// <summary> /// <summary>
/// Prototype object for creating or altering users /// Prototype object for creating or altering users
/// </summary> /// </summary>
internal class UserPrototypeNew : IUserPrototype internal class UserPrototype : IUserPrototype
{ {
protected UserPrototypeDataNew originalState = null; protected UserPrototypeData originalState;
protected UserPrototypeDataNew currentState = null; protected UserPrototypeData currentState;
private List<string> schemaNames = null; private List<string> schemaNames;
private List<string> roleNames = null; private List<string> roleNames;
private bool exists = false; private bool exists = false;
private Database parent = null; private Database parent;
public bool IsRoleMembershipChangesApplied { get; set; } //default is false public bool IsRoleMembershipChangesApplied { get; set; } //default is false
public bool IsSchemaOwnershipChangesApplied { get; set; } //default is false public bool IsSchemaOwnershipChangesApplied { get; set; } //default is false
@@ -412,23 +437,28 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
/// Constructor /// Constructor
/// </summary> /// </summary>
/// <param name="context">The context for the dialog</param> /// <param name="context">The context for the dialog</param>
public UserPrototypeNew(CDataContainer context, public UserPrototype(CDataContainer context,
UserPrototypeDataNew current, UserPrototypeData current,
UserPrototypeDataNew original) UserPrototypeData original)
{ {
this.currentState = current; this.currentState = current;
this.originalState = original; this.originalState = original;
this.exists = !context.IsNewObject; this.exists = !context.IsNewObject;
this.parent = context.Server.GetSmoObject(new Urn(context.ParentUrn)) as Database;
Database? parent = context.Server.GetSmoObject(new Urn(context.ParentUrn)) as Database;
this.PopulateRoles(); if (parent == null)
this.PopulateSchemas(); {
throw new ArgumentException("Context ParentUrn is invalid");
}
this.parent = parent;
this.roleNames = this.PopulateRoles();
this.schemaNames = this.PopulateSchemas();
} }
private void PopulateRoles() private List<string> PopulateRoles()
{ {
this.roleNames = new List<string>(); var roleNames = new List<string>();
foreach (DatabaseRole dbRole in this.parent.Roles) foreach (DatabaseRole dbRole in this.parent.Roles)
{ {
@@ -438,22 +468,24 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
this.roleNames.Add(dbRole.Name); this.roleNames.Add(dbRole.Name);
} }
} }
return roleNames;
} }
private void PopulateSchemas() private List<string> PopulateSchemas()
{ {
this.schemaNames = new List<string>(); var schemaNames = new List<string>();
if (!SqlMgmtUtils.IsYukonOrAbove(this.parent.Parent) if (!SqlMgmtUtils.IsYukonOrAbove(this.parent.Parent)
|| this.parent.CompatibilityLevel <= CompatibilityLevel.Version80) || this.parent.CompatibilityLevel <= CompatibilityLevel.Version80)
{ {
return; throw new ArgumentException("Unsupported server version");
} }
foreach (Schema sch in this.parent.Schemas) foreach (Schema sch in this.parent.Schemas)
{ {
this.schemaNames.Add(sch.Name); this.schemaNames.Add(sch.Name);
} }
return schemaNames;
} }
public bool IsYukonOrLater public bool IsYukonOrLater
@@ -466,9 +498,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
public User ApplyChanges() public User ApplyChanges()
{ {
User user = null; User user = this.GetUser();
user = this.GetUser();
if (this.ChangesExist()) if (this.ChangesExist())
{ {
@@ -504,50 +534,56 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
private void ApplySchemaOwnershipChanges(User user) private void ApplySchemaOwnershipChanges(User user)
{ {
IEnumerator<KeyValuePair<string, bool>> enumerator = this.currentState.isSchemaOwned.GetEnumerator(); IEnumerator<KeyValuePair<string, bool>>? enumerator = this.currentState.isSchemaOwned?.GetEnumerator();
enumerator.Reset(); if (enumerator != null)
String nullString = null;
while (enumerator.MoveNext())
{ {
string schemaName = enumerator.Current.Key.ToString(); enumerator.Reset();
bool userIsOwner = (bool)enumerator.Current.Value;
if (((bool)this.originalState.isSchemaOwned[schemaName]) != userIsOwner) String? nullString = null;
while (enumerator.MoveNext())
{ {
System.Diagnostics.Debug.Assert(!this.Exists || userIsOwner, "shouldn't have to unset ownership for new users"); string schemaName = enumerator.Current.Key.ToString();
bool userIsOwner = (bool)enumerator.Current.Value;
Schema schema = this.parent.Schemas[schemaName]; if (this.originalState.isSchemaOwned?[schemaName] != userIsOwner)
schema.Owner = userIsOwner ? user.Name : nullString; {
schema.Alter(); System.Diagnostics.Debug.Assert(!this.Exists || userIsOwner, "shouldn't have to unset ownership for new users");
Schema schema = this.parent.Schemas[schemaName];
schema.Owner = userIsOwner ? user.Name : nullString;
schema.Alter();
}
} }
} }
} }
private void ApplyRoleMembershipChanges(User user) private void ApplyRoleMembershipChanges(User user)
{ {
IEnumerator<KeyValuePair<string, bool>> enumerator = this.currentState.isMember.GetEnumerator(); IEnumerator<KeyValuePair<string, bool>>? enumerator = this.currentState.isMember?.GetEnumerator();
enumerator.Reset(); if (enumerator != null)
while (enumerator.MoveNext())
{ {
string roleName = enumerator.Current.Key; enumerator.Reset();
bool userIsMember = (bool)enumerator.Current.Value;
if (((bool)this.originalState.isMember[roleName]) != userIsMember) while (enumerator.MoveNext())
{ {
System.Diagnostics.Debug.Assert(this.Exists || userIsMember, "shouldn't have to unset membership for new users"); string roleName = enumerator.Current.Key;
bool userIsMember = enumerator.Current.Value;
DatabaseRole role = this.parent.Roles[roleName]; if (this.originalState.isMember?[roleName] != userIsMember)
{
System.Diagnostics.Debug.Assert(this.Exists || userIsMember, "shouldn't have to unset membership for new users");
if (userIsMember) DatabaseRole role = this.parent.Roles[roleName];
{
role.AddMember(user.Name); if (userIsMember)
} {
else role.AddMember(user.Name);
{ }
role.DropMember(user.Name); else
{
role.DropMember(user.Name);
}
} }
} }
} }
@@ -577,13 +613,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
public User GetUser() public User GetUser()
{ {
User result = null; User result;
// if we think we exist, get the SMO user object // if we think we exist, get the SMO user object
if (this.Exists) if (this.Exists)
{ {
result = this.parent.Users[this.originalState.name]; result = this.parent.Users[this.originalState.name];
result.Refresh(); result?.Refresh();
System.Diagnostics.Debug.Assert(0 == String.Compare(this.originalState.name, this.currentState.name, StringComparison.Ordinal), "name of existing user has changed"); System.Diagnostics.Debug.Assert(0 == String.Compare(this.originalState.name, this.currentState.name, StringComparison.Ordinal), "name of existing user has changed");
if (result == null) if (result == null)
@@ -613,7 +649,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
} }
} }
internal class UserPrototypeWithDefaultSchema : UserPrototypeNew, internal class UserPrototypeWithDefaultSchema : UserPrototype,
IUserPrototypeWithDefaultSchema IUserPrototypeWithDefaultSchema
{ {
private CDataContainer context; private CDataContainer context;
@@ -648,8 +684,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
/// </summary> /// </summary>
/// <param name="context">The context for the dialog</param> /// <param name="context">The context for the dialog</param>
public UserPrototypeWithDefaultSchema(CDataContainer context, public UserPrototypeWithDefaultSchema(CDataContainer context,
UserPrototypeDataNew current, UserPrototypeData current,
UserPrototypeDataNew original) UserPrototypeData original)
: base(context, current, original) : base(context, current, original)
{ {
this.context = context; this.context = context;
@@ -692,8 +728,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
/// </summary> /// </summary>
/// <param name="context">The context for the dialog</param> /// <param name="context">The context for the dialog</param>
public UserPrototypeForSqlUserWithLogin(CDataContainer context, public UserPrototypeForSqlUserWithLogin(CDataContainer context,
UserPrototypeDataNew current, UserPrototypeData current,
UserPrototypeDataNew original) UserPrototypeData original)
: base(context, current, original) : base(context, current, original)
{ {
} }
@@ -719,9 +755,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
get get
{ {
//Default Schema was not supported before Denali for windows group. //Default Schema was not supported before Denali for windows group.
User user = null; User user = this.GetUser();
user = this.GetUser();
if (this.Exists && user.LoginType == LoginType.WindowsGroup) if (this.Exists && user.LoginType == LoginType.WindowsGroup)
{ {
return SqlMgmtUtils.IsSql11OrLater(this.context.Server.ConnectionContext.ServerVersion); return SqlMgmtUtils.IsSql11OrLater(this.context.Server.ConnectionContext.ServerVersion);
@@ -763,8 +797,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
/// </summary> /// </summary>
/// <param name="context">The context for the dialog</param> /// <param name="context">The context for the dialog</param>
public UserPrototypeForWindowsUser(CDataContainer context, public UserPrototypeForWindowsUser(CDataContainer context,
UserPrototypeDataNew current, UserPrototypeData current,
UserPrototypeDataNew original) UserPrototypeData original)
: base(context, current, original) : base(context, current, original)
{ {
this.context = context; this.context = context;
@@ -869,8 +903,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
/// </summary> /// </summary>
/// <param name="context">The context for the dialog</param> /// <param name="context">The context for the dialog</param>
public UserPrototypeForSqlUserWithPassword(CDataContainer context, public UserPrototypeForSqlUserWithPassword(CDataContainer context,
UserPrototypeDataNew current, UserPrototypeData current,
UserPrototypeDataNew original) UserPrototypeData original)
: base(context, current, original) : base(context, current, original)
{ {
this.context = context; this.context = context;
@@ -933,26 +967,26 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
/// </summary> /// </summary>
internal class UserPrototypeFactory internal class UserPrototypeFactory
{ {
private static UserPrototypeFactory singletonInstance; private static UserPrototypeFactory? singletonInstance;
private UserPrototypeDataNew currentData; private UserPrototypeData currentData;
private UserPrototypeDataNew originalData; private UserPrototypeData originalData;
private CDataContainer context; private CDataContainer context;
private UserPrototypeNew asymmetricKeyMappedUser; private UserPrototype? asymmetricKeyMappedUser;
private UserPrototypeNew certificateMappedUser; private UserPrototype? certificateMappedUser;
private UserPrototypeNew loginMappedUser; private UserPrototype? loginMappedUser;
private UserPrototypeNew noLoginUser; private UserPrototype? noLoginUser;
private UserPrototypeNew sqlUserWithPassword; private UserPrototype? sqlUserWithPassword;
private UserPrototypeNew windowsUser; private UserPrototype? windowsUser;
private UserPrototypeNew currentPrototype; private UserPrototype? currentPrototype;
public UserPrototypeNew CurrentPrototype public UserPrototype CurrentPrototype
{ {
get get
{ {
currentPrototype ??= new UserPrototypeNew(this.context, currentPrototype ??= new UserPrototype(this.context,
this.currentData, this.currentData,
this.originalData); this.originalData);
return currentPrototype; return currentPrototype;
@@ -963,7 +997,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
{ {
this.context = context; this.context = context;
this.originalData = new UserPrototypeDataNew(this.context); this.originalData = new UserPrototypeData(this.context);
this.currentData = this.originalData.Clone(); this.currentData = this.originalData.Clone();
} }
@@ -980,19 +1014,19 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
return singletonInstance; return singletonInstance;
} }
public UserPrototypeNew GetUserPrototype(ExhaustiveUserTypes userType) public UserPrototype GetUserPrototype(ExhaustiveUserTypes userType)
{ {
switch (userType) switch (userType)
{ {
case ExhaustiveUserTypes.AsymmetricKeyMappedUser: case ExhaustiveUserTypes.AsymmetricKeyMappedUser:
currentData.userType = UserType.AsymmetricKey; currentData.userType = UserType.AsymmetricKey;
this.asymmetricKeyMappedUser ??= new UserPrototypeNew(this.context, this.currentData, this.originalData); this.asymmetricKeyMappedUser ??= new UserPrototype(this.context, this.currentData, this.originalData);
this.currentPrototype = asymmetricKeyMappedUser; this.currentPrototype = asymmetricKeyMappedUser;
break; break;
case ExhaustiveUserTypes.CertificateMappedUser: case ExhaustiveUserTypes.CertificateMappedUser:
currentData.userType = UserType.Certificate; currentData.userType = UserType.Certificate;
this.certificateMappedUser ??= new UserPrototypeNew(this.context, this.currentData, this.originalData); this.certificateMappedUser ??= new UserPrototype(this.context, this.currentData, this.originalData);
this.currentPrototype = certificateMappedUser; this.currentPrototype = certificateMappedUser;
break; break;