//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.Management;
using Microsoft.SqlTools.ServiceLayer.Security.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.Security
{
///
/// Main class for Security Service functionality
///
public sealed class SecurityService : IDisposable
{
private bool disposed;
private ConnectionService? connectionService;
private UserServiceHandlerImpl userServiceHandler;
private LoginServiceHandlerImpl loginServiceHandler;
private static readonly Lazy instance = new Lazy(() => new SecurityService());
///
/// Construct a new SecurityService instance with default parameters
///
public SecurityService()
{
userServiceHandler = new UserServiceHandlerImpl();
loginServiceHandler = new LoginServiceHandlerImpl();
}
///
/// Gets the singleton instance object
///
public static SecurityService Instance
{
get { return instance.Value; }
}
///
/// Internal for testing purposes only
///
internal ConnectionService ConnectionServiceInstance
{
get
{
connectionService ??= ConnectionService.Instance;
return connectionService;
}
set
{
connectionService = value;
}
}
///
/// Service host object for sending/receiving requests/events.
/// Internal for testing purposes.
///
internal IProtocolEndpoint? ServiceHost
{
get;
set;
}
///
/// Initializes the Security Service instance
///
public void InitializeService(ServiceHost serviceHost)
{
this.ServiceHost = serviceHost;
// Credential request handlers
this.ServiceHost.SetRequestHandler(CreateCredentialRequest.Type, HandleCreateCredentialRequest, true);
this.ServiceHost.SetRequestHandler(UpdateCredentialRequest.Type, HandleUpdateCredentialRequest, true);
this.ServiceHost.SetRequestHandler(GetCredentialsRequest.Type, HandleGetCredentialsRequest, true);
// Login request handlers
this.ServiceHost.SetRequestHandler(CreateLoginRequest.Type, this.loginServiceHandler.HandleCreateLoginRequest, true);
this.ServiceHost.SetRequestHandler(UpdateLoginRequest.Type, this.loginServiceHandler.HandleUpdateLoginRequest, true);
this.ServiceHost.SetRequestHandler(InitializeLoginViewRequest.Type, this.loginServiceHandler.HandleInitializeLoginViewRequest, true);
this.ServiceHost.SetRequestHandler(DisposeLoginViewRequest.Type, this.loginServiceHandler.HandleDisposeLoginViewRequest, true);
// User request handlers
this.ServiceHost.SetRequestHandler(InitializeUserViewRequest.Type, this.userServiceHandler.HandleInitializeUserViewRequest, true);
this.ServiceHost.SetRequestHandler(CreateUserRequest.Type, this.userServiceHandler.HandleCreateUserRequest, true);
this.ServiceHost.SetRequestHandler(UpdateUserRequest.Type, this.userServiceHandler.HandleUpdateUserRequest, true);
this.ServiceHost.SetRequestHandler(ScriptUserRequest.Type, this.userServiceHandler.HandleScriptUserRequest, true);
this.ServiceHost.SetRequestHandler(DisposeUserViewRequest.Type, this.userServiceHandler.HandleDisposeUserViewRequest, true);
}
#region "Credential Handlers"
///
/// Handle request to create a credential
///
internal async Task HandleCreateCredentialRequest(CreateCredentialParams parameters, RequestContext requestContext)
{
var result = await ConfigureCredential(parameters.OwnerUri,
parameters.Credential,
ConfigAction.Create,
RunType.RunNow);
await requestContext.SendResult(new CredentialResult()
{
Credential = parameters.Credential,
Success = result.Item1,
ErrorMessage = result.Item2
});
}
///
/// Handle request to update a credential
///
internal async Task HandleUpdateCredentialRequest(UpdateCredentialParams parameters, RequestContext requestContext)
{
var result = await ConfigureCredential(parameters.OwnerUri,
parameters.Credential,
ConfigAction.Update,
RunType.RunNow);
await requestContext.SendResult(new CredentialResult()
{
Credential = parameters.Credential,
Success = result.Item1,
ErrorMessage = result.Item2
});
}
///
/// Handle request to get all credentials
///
internal async Task HandleGetCredentialsRequest(GetCredentialsParams parameters, RequestContext requestContext)
{
var result = new GetCredentialsResult();
try
{
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(parameters.OwnerUri, out connInfo);
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
var credentials = dataContainer.Server?.Credentials;
int credentialsCount = credentials != null ? credentials.Count : 0;
CredentialInfo[] credentialsInfos = new CredentialInfo[credentialsCount];
if (credentials != null)
{
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;
}
catch (Exception ex)
{
result.Success = false;
result.ErrorMessage = ex.ToString();
}
await requestContext.SendResult(result);
}
///
/// Disposes the service
///
public void Dispose()
{
if (!disposed)
{
disposed = true;
}
}
internal Task> ConfigureCredential(
string ownerUri,
CredentialInfo credential,
ConfigAction configAction,
RunType runType)
{
return Task>.Run(() =>
{
try
{
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(ownerUri, out connInfo);
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
using (CredentialActions actions = new CredentialActions(dataContainer, credential, configAction))
{
var executionHandler = new ExecutonHandler(actions);
executionHandler.RunNow(runType, this);
}
return new Tuple(true, string.Empty);
}
catch (Exception ex)
{
return new Tuple(false, ex.ToString());
}
});
}
#endregion // "Credential Handlers"
}
}