mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-14 01:25:40 -05:00
Add login management handlers (#1868)
* update contracts * finish creating/loading login for SQL Server * support role read for azure and add more handlers * fix advanced option flags --------- Co-authored-by: Karl Burtram <karlb@microsoft.com>
This commit is contained in:
@@ -93,7 +93,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
||||
|
||||
// Login request handlers
|
||||
this.ServiceHost.SetRequestHandler(CreateLoginRequest.Type, HandleCreateLoginRequest, true);
|
||||
this.ServiceHost.SetRequestHandler(UpdateLoginRequest.Type, HandleUpdateLoginRequest, true);
|
||||
this.ServiceHost.SetRequestHandler(DeleteLoginRequest.Type, HandleDeleteLoginRequest, true);
|
||||
this.ServiceHost.SetRequestHandler(InitializeLoginViewRequest.Type, HandleInitializeLoginViewRequest, true);
|
||||
this.ServiceHost.SetRequestHandler(DisposeLoginViewRequest.Type, HandleDisposeLoginViewRequest, true);
|
||||
|
||||
// User request handlers
|
||||
this.ServiceHost.SetRequestHandler(InitializeUserViewRequest.Type, HandleInitializeUserViewRequest, true);
|
||||
@@ -107,14 +110,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
||||
/// <summary>
|
||||
/// Handle request to create a login
|
||||
/// </summary>
|
||||
internal async Task HandleCreateLoginRequest(CreateLoginParams parameters, RequestContext<CreateLoginResult> requestContext)
|
||||
internal async Task HandleCreateLoginRequest(CreateLoginParams parameters, RequestContext<object> requestContext)
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
ConnectionServiceInstance.TryFindConnection(parameters.OwnerUri, out connInfo);
|
||||
// if (connInfo == null)
|
||||
// {
|
||||
// // raise an error
|
||||
// }
|
||||
string ownerUri;
|
||||
contextIdToConnectionUriMap.TryGetValue(parameters.ContextId, out ownerUri);
|
||||
ConnectionServiceInstance.TryFindConnection(ownerUri, out connInfo);
|
||||
|
||||
if (connInfo == null)
|
||||
{
|
||||
// raise error here
|
||||
}
|
||||
|
||||
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
||||
LoginPrototype prototype = new LoginPrototype(dataContainer.Server, parameters.Login);
|
||||
@@ -127,7 +133,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
||||
// return the error associated with null password (coming from policy) - see bug 124377
|
||||
if (prototype.SqlPassword.Length == 0 && prototype.EnforcePolicy == false)
|
||||
{
|
||||
// raise error here
|
||||
// raise error here
|
||||
}
|
||||
|
||||
// check that password and confirm password controls' text matches
|
||||
@@ -139,29 +145,35 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
||||
|
||||
prototype.ApplyGeneralChanges(dataContainer.Server);
|
||||
|
||||
await requestContext.SendResult(new CreateLoginResult()
|
||||
// TODO move this to LoginData
|
||||
// TODO support role assignment for Azure
|
||||
LoginPrototype newPrototype = new LoginPrototype(dataContainer.Server, dataContainer.Server.Logins[parameters.Login.Name]);
|
||||
var _ =newPrototype.ServerRoles.ServerRoleNames;
|
||||
|
||||
foreach (string role in parameters.Login.ServerRoles)
|
||||
{
|
||||
Login = parameters.Login,
|
||||
Success = true,
|
||||
ErrorMessage = string.Empty
|
||||
});
|
||||
newPrototype.ServerRoles.SetMember(role, true);
|
||||
}
|
||||
|
||||
newPrototype.ApplyServerRoleChanges(dataContainer.Server);
|
||||
await requestContext.SendResult(new object());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle request to delete a credential
|
||||
/// </summary>
|
||||
internal async Task HandleDeleteLoginRequest(DeleteLoginParams parameters, RequestContext<ResultStatus> requestContext)
|
||||
internal async Task HandleDeleteLoginRequest(DeleteLoginParams parameters, RequestContext<object> requestContext)
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
ConnectionServiceInstance.TryFindConnection(parameters.OwnerUri, out connInfo);
|
||||
ConnectionServiceInstance.TryFindConnection(parameters.ConnectionUri, out connInfo);
|
||||
// if (connInfo == null)
|
||||
// {
|
||||
// // raise an error
|
||||
// }
|
||||
|
||||
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
||||
Login login = dataContainer.Server?.Logins[parameters.LoginName];
|
||||
|
||||
Login login = dataContainer.Server?.Logins[parameters.Name];
|
||||
|
||||
dataContainer.SqlDialogSubject = login;
|
||||
DoDropObject(dataContainer);
|
||||
|
||||
@@ -172,6 +184,152 @@ namespace Microsoft.SqlTools.ServiceLayer.Security
|
||||
});
|
||||
}
|
||||
|
||||
internal async Task HandleUpdateLoginRequest(UpdateLoginParams parameters, RequestContext<object> requestContext)
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
string ownerUri;
|
||||
contextIdToConnectionUriMap.TryGetValue(parameters.ContextId, out ownerUri);
|
||||
ConnectionServiceInstance.TryFindConnection(ownerUri, out connInfo);
|
||||
if (connInfo == null)
|
||||
{
|
||||
// raise error here
|
||||
}
|
||||
|
||||
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
||||
LoginPrototype prototype = new LoginPrototype(dataContainer.Server, dataContainer.Server.Logins[parameters.Login.Name]);
|
||||
|
||||
var login = parameters.Login;
|
||||
prototype.SqlPassword = login.Password;
|
||||
prototype.DefaultLanguage = login.DefaultLanguage;
|
||||
prototype.DefaultDatabase = login.DefaultDatabase;
|
||||
prototype.EnforcePolicy = login.EnforcePasswordPolicy;
|
||||
prototype.EnforceExpiration = login.EnforcePasswordPolicy ? login.EnforcePasswordExpiration : false;
|
||||
prototype.IsLockedOut = login.IsLockedOut;
|
||||
prototype.IsDisabled = !login.IsEnabled;
|
||||
prototype.MustChange = login.EnforcePasswordPolicy ? login.MustChangePassword : false;
|
||||
prototype.WindowsGrantAccess = login.ConnectPermission;
|
||||
|
||||
if (prototype.LoginType == SqlServer.Management.Smo.LoginType.SqlLogin)
|
||||
{
|
||||
// check that there is a password
|
||||
// this check is made if policy enforcement is off
|
||||
// with policy turned on we do not display this message, instead we let server
|
||||
// return the error associated with null password (coming from policy) - see bug 124377
|
||||
if (prototype.SqlPassword.Length == 0 && prototype.EnforcePolicy == false)
|
||||
{
|
||||
// raise error here
|
||||
}
|
||||
|
||||
// check that password and confirm password controls' text matches
|
||||
if (0 != String.Compare(prototype.SqlPassword, prototype.SqlPasswordConfirm, StringComparison.Ordinal))
|
||||
{
|
||||
// raise error here
|
||||
}
|
||||
}
|
||||
|
||||
var _ = prototype.ServerRoles.ServerRoleNames;
|
||||
foreach (string role in login.ServerRoles)
|
||||
{
|
||||
prototype.ServerRoles.SetMember(role, true);
|
||||
}
|
||||
|
||||
prototype.ApplyGeneralChanges(dataContainer.Server);
|
||||
prototype.ApplyServerRoleChanges(dataContainer.Server);
|
||||
prototype.ApplyDatabaseRoleChanges(dataContainer.Server);
|
||||
await requestContext.SendResult(new object());
|
||||
}
|
||||
|
||||
internal async Task HandleInitializeLoginViewRequest(InitializeLoginViewRequestParams parameters, RequestContext<LoginViewInfo> requestContext)
|
||||
{
|
||||
contextIdToConnectionUriMap.Add(parameters.ContextId, parameters.ConnectionUri);
|
||||
ConnectionInfo connInfo;
|
||||
ConnectionServiceInstance.TryFindConnection(parameters.ConnectionUri, out connInfo);
|
||||
if (connInfo == null)
|
||||
{
|
||||
// raise an error
|
||||
}
|
||||
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
||||
LoginViewInfo loginViewInfo = new LoginViewInfo();
|
||||
|
||||
string[] databases = new string[dataContainer.Server.Databases.Count];
|
||||
for (int i = 0; i < dataContainer.Server.Databases.Count; i++)
|
||||
{
|
||||
databases[i] = dataContainer.Server.Databases[i].Name;
|
||||
}
|
||||
string[] languages = new string[dataContainer.Server.Languages.Count];
|
||||
for (int i = 0; i < dataContainer.Server.Languages.Count; i++)
|
||||
{
|
||||
languages[i] = dataContainer.Server.Languages[i].Name;
|
||||
}
|
||||
|
||||
LoginPrototype prototype = parameters.IsNewObject
|
||||
? new LoginPrototype(dataContainer.Server)
|
||||
: new LoginPrototype(dataContainer.Server, dataContainer.Server.Logins[parameters.Name]);
|
||||
|
||||
List<string> loginServerRoles = new List<string>();
|
||||
foreach(string role in prototype.ServerRoles.ServerRoleNames)
|
||||
{
|
||||
if (prototype.ServerRoles.IsMember(role))
|
||||
{
|
||||
loginServerRoles.Add(role);
|
||||
}
|
||||
}
|
||||
|
||||
LoginInfo loginInfo = new LoginInfo()
|
||||
{
|
||||
Name = prototype.LoginName,
|
||||
Password = prototype.SqlPassword,
|
||||
OldPassword = prototype.OldPassword,
|
||||
AuthenticationType = LoginTypeToAuthenticationType(prototype.LoginType),
|
||||
EnforcePasswordExpiration = prototype.EnforceExpiration,
|
||||
EnforcePasswordPolicy = prototype.EnforcePolicy,
|
||||
MustChangePassword = prototype.MustChange,
|
||||
DefaultDatabase = prototype.DefaultDatabase,
|
||||
DefaultLanguage = prototype.DefaultDatabase,
|
||||
ServerRoles = loginServerRoles.ToArray(),
|
||||
ConnectPermission = prototype.WindowsGrantAccess,
|
||||
IsEnabled = !prototype.IsDisabled,
|
||||
IsLockedOut = prototype.IsLockedOut,
|
||||
UserMapping = new ServerLoginDatabaseUserMapping[0]
|
||||
};
|
||||
|
||||
await requestContext.SendResult(new LoginViewInfo()
|
||||
{
|
||||
ObjectInfo = loginInfo,
|
||||
SupportWindowsAuthentication = prototype.WindowsAuthSupported,
|
||||
SupportAADAuthentication = prototype.AADAuthSupported,
|
||||
SupportSQLAuthentication = true, // SQL Auth support for login, not necessarily mean SQL Auth support for CONNECT etc.
|
||||
CanEditLockedOutState = true,
|
||||
Databases = databases,
|
||||
Languages = languages,
|
||||
ServerRoles = prototype.ServerRoles.ServerRoleNames,
|
||||
SupportAdvancedPasswordOptions = dataContainer.Server.DatabaseEngineType == DatabaseEngineType.Standalone || dataContainer.Server.DatabaseEngineEdition == DatabaseEngineEdition.SqlDataWarehouse,
|
||||
SupportAdvancedOptions = dataContainer.Server.DatabaseEngineType == DatabaseEngineType.Standalone || dataContainer.Server.DatabaseEngineEdition == DatabaseEngineEdition.SqlManagedInstance
|
||||
});
|
||||
}
|
||||
|
||||
private LoginAuthenticationType LoginTypeToAuthenticationType(LoginType loginType)
|
||||
{
|
||||
switch (loginType)
|
||||
{
|
||||
case LoginType.WindowsUser:
|
||||
case LoginType.WindowsGroup:
|
||||
return LoginAuthenticationType.Windows;
|
||||
case LoginType.SqlLogin:
|
||||
return LoginAuthenticationType.Sql;
|
||||
case LoginType.ExternalUser:
|
||||
case LoginType.ExternalGroup:
|
||||
return LoginAuthenticationType.AAD;
|
||||
default:
|
||||
return LoginAuthenticationType.Others;
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task HandleDisposeLoginViewRequest(DisposeLoginViewRequestParams parameters, RequestContext<object> requestContext)
|
||||
{
|
||||
contextIdToConnectionUriMap.Remove(parameters.ContextId);
|
||||
await requestContext.SendResult(new object());
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "User Handlers"
|
||||
|
||||
Reference in New Issue
Block a user