408 lines
20 KiB
C#
408 lines
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Sdaleo;
|
|
using System.Net;
|
|
using Yaulw.Xml;
|
|
using Yaulw.File;
|
|
using CrossProduct.Core;
|
|
using System.Data;
|
|
using Sdaleo.Systems.SQLServer;
|
|
using Sdaleo.Systems.Advantage;
|
|
using Sdaleo.Systems;
|
|
using Pluto.Api;
|
|
|
|
namespace PlutoServer.MSL.Connectors
|
|
{
|
|
/// <summary>
|
|
/// Responsible for cachine SystemAPIKeys to physical Databases/Connections
|
|
/// Since we are now seperated from the instance, the practice needs to be looked up
|
|
/// in the practice list table. The Practice List Table will contain UserApiKeys that we
|
|
/// have to convert to SystemApiKeys. This cache will save us a lookup, as well as do the work for us
|
|
/// </summary>
|
|
public static class DBCache
|
|
{
|
|
private static Dictionary<string, SQLServerCredential> _LytecConnetions = new Dictionary<string, SQLServerCredential>();
|
|
private static Dictionary<string, AdvantageCredential> _MedisoftConnections = new Dictionary<string, AdvantageCredential>();
|
|
|
|
// Static Instance of the Current Configuration Data
|
|
private static DBCacheDataStore s_DBCredentialCacheDataStore = new DBCacheDataStore();
|
|
private static XSerializer s_xmlserializer = new XSerializer();
|
|
private static ISReadWrite s_ISReadWrite = new ISReadWrite("DBCache.enc");
|
|
|
|
#region Construction
|
|
|
|
static DBCache()
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Mobile Test Settings
|
|
|
|
/// <summary>
|
|
/// Is this computer a computer used for mobile testing?
|
|
/// </summary>
|
|
public static bool IsMachine_Used_ForTesting = false;
|
|
|
|
/// <summary>
|
|
/// Used for Unit Testing Purposes
|
|
/// </summary>
|
|
public static SQLServerCredential TestLytecUserDBCredential = null;
|
|
|
|
/// <summary>
|
|
/// Used for Unit Testing Purposes
|
|
/// </summary>
|
|
public static AdvantageCredential TestMedisoftDBCredential = null;
|
|
|
|
#endregion
|
|
|
|
#region AddSharedDataConnections (Public for Test Framework)
|
|
|
|
/// <summary>
|
|
/// Add a new SQL Server Credential to the datastore if it doesn't already exist
|
|
/// </summary>
|
|
/// <param name="bSaveToFile">true to save to the DataStore File</param>
|
|
public static void AddSharedDataConnectionToDataStore(SQLServerCredential credential, bool bSaveToFile)
|
|
{
|
|
// Check if exists
|
|
DBCacheDataStore.Credential[] existing_creds = s_DBCredentialCacheDataStore.SQLServerCredentials.Credentials;
|
|
foreach (DBCacheDataStore.Credential existing_cred in existing_creds)
|
|
{
|
|
if (String.Compare(credential.Server, existing_cred.SqlServer, true) == 0 &&
|
|
String.Compare(credential.Instance, existing_cred.SqlInstance, true) == 0)
|
|
return;
|
|
}
|
|
|
|
// else Add to persisten store
|
|
DBCacheDataStore.Credential cred = new DBCacheDataStore.Credential();
|
|
cred.SqlServer = credential.Server;
|
|
cred.SqlInstance = credential.Instance;
|
|
cred.SqlUser = credential.User;
|
|
cred.SqlPassword = ConnStr.RetrieveValue("PASSWORD", credential.ConnectionString);
|
|
s_DBCredentialCacheDataStore.SQLServerCredentials.AddCredential(cred);
|
|
|
|
// Log this
|
|
MSLSpecific.Logger.Info("Adding a new SQL Server Credential to the Datastore for Server:{0} Instance:{1}", cred.SqlServer, cred.SqlInstance);
|
|
|
|
// Save to File
|
|
if (bSaveToFile)
|
|
SaveDataStore();
|
|
|
|
// Load all User Api Keys for new Connection
|
|
IterateSharedConnectionAndLoadUserApiKeyConnections(credential);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a new Advantage Credential * Shared Credential * to the datastore if it doesn't already exist
|
|
/// </summary>
|
|
/// <param name="bSaveToFile">true to save to the DataStore File</param>
|
|
public static void AddSharedDataConnectionToDataStore(AdvantageCredential credential, bool bSaveToFile)
|
|
{
|
|
// Check if exists
|
|
DBCacheDataStore.Credential[] existing_creds = s_DBCredentialCacheDataStore.AdvantageCredentials.Credentials;
|
|
foreach (DBCacheDataStore.Credential existing_cred in existing_creds)
|
|
{
|
|
if (String.Compare(credential.DataSource, existing_cred.AdvDataSource, true) == 0)
|
|
return;
|
|
}
|
|
|
|
// else Add to persisten store
|
|
DBCacheDataStore.Credential cred = new DBCacheDataStore.Credential();
|
|
cred.AdvDataSource = credential.DataSource;
|
|
cred.AdvIsRemote = (ConnStr.RetrieveValue("SERVERTYPE", credential.ConnectionString) == "REMOTE");
|
|
s_DBCredentialCacheDataStore.AdvantageCredentials.AddCredential(cred);
|
|
|
|
// Log this
|
|
MSLSpecific.Logger.Info("Adding a new Advantage Credential to the Datastore DataSource:{0} Remote:{1}", cred.AdvDataSource, cred.AdvIsRemote);
|
|
|
|
// Save to File
|
|
if (bSaveToFile)
|
|
SaveDataStore();
|
|
|
|
// Load all User Api Keys for new Connection
|
|
IterateSharedConnectionAndLoadUserApiKeyConnections(credential);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Persistent DBCache File Store (Internal)
|
|
|
|
/// <summary>
|
|
/// Read from the DataStore into user connections * Reads in all credentials from a file into SQLServer & Medisoft Connections *
|
|
/// Additionally, pulls out UserApiKeys from the Practice List of the corresponding Databases
|
|
/// </summary>
|
|
/// <returns>true if file doesn't exist or if successfull read occured, false otherwise</returns>
|
|
internal static bool ReadInDataStore()
|
|
{
|
|
try
|
|
{
|
|
string ISFileContent = s_ISReadWrite.ReadFromIS();
|
|
if (!String.IsNullOrEmpty(ISFileContent))
|
|
{
|
|
// Ran into the issue that the data in the DBCache is total nonsense after decrypting,
|
|
// not sure yet how to doeal with yet * however if that is the case * what else can we
|
|
// do but ignore the input?
|
|
try
|
|
{
|
|
ISFileContent = Encryption.DecryptText(ISFileContent);
|
|
s_DBCredentialCacheDataStore = s_xmlserializer.ReadFromString<DBCacheDataStore>(ISFileContent);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
// error occured parsing the file, ignore the read!
|
|
PlutoService.AppLogError(String.Format("DBCache File is invalid. Ignoring and erasing the cache: {0}", e.Message));
|
|
s_ISReadWrite.WriteToIS(String.Empty);
|
|
return true;
|
|
}
|
|
|
|
// Test to see if a single successful read occured
|
|
bool bASuccessFullLytecReadOccured = false;
|
|
// Iterate thru all the SQL Connections
|
|
_LytecConnetions.Clear();
|
|
bool bLytecConnectionsFound = false;
|
|
int nLytecCount = 0;
|
|
foreach (DBCacheDataStore.Credential credential in s_DBCredentialCacheDataStore.SQLServerCredentials.Credentials)
|
|
{
|
|
if (credential != null)
|
|
{
|
|
bLytecConnectionsFound = true;
|
|
SQLServerCredential tempCred = new SQLServerCredential(credential.SqlServer, credential.SqlInstance, "Lytec SharedData", credential.SqlUser, credential.SqlPassword);
|
|
bool bSuccess = IterateSharedConnectionAndLoadUserApiKeyConnections(tempCred);
|
|
if (bSuccess)
|
|
{
|
|
bASuccessFullLytecReadOccured = true;
|
|
nLytecCount++;
|
|
}
|
|
}
|
|
}
|
|
if (bLytecConnectionsFound && !bASuccessFullLytecReadOccured)
|
|
PlutoService.AppLogInfo("Not one valid Lytec Connection was found in the DataStore");
|
|
if(nLytecCount > 0)
|
|
PlutoService.AppLogInfo(String.Format("Loaded {0} Lytec Shared Connection",nLytecCount));
|
|
|
|
// Test to see if a single successful read occured
|
|
bool bASuccessFullMedisoftReadOccured = false;
|
|
// Iterate thru all the Advantage Connections
|
|
_MedisoftConnections.Clear();
|
|
bool bMedisoftConnectionsFound = false;
|
|
int nMedisoftCount = 0;
|
|
foreach (DBCacheDataStore.Credential credential in s_DBCredentialCacheDataStore.AdvantageCredentials.Credentials)
|
|
{
|
|
if (credential != null)
|
|
{
|
|
bMedisoftConnectionsFound = true;
|
|
AdvantageCredential tempCred = new AdvantageCredential(credential.AdvDataSource, "SharedDataUser", "AndPassword", credential.AdvIsRemote ? AdvantageCredential.ServerType.REMOTE : AdvantageCredential.ServerType.LOCAL);
|
|
bool bSuccess = IterateSharedConnectionAndLoadUserApiKeyConnections(tempCred);
|
|
if (bSuccess)
|
|
{
|
|
bASuccessFullMedisoftReadOccured = true;
|
|
nMedisoftCount++;
|
|
}
|
|
}
|
|
}
|
|
if (bMedisoftConnectionsFound && !bASuccessFullMedisoftReadOccured)
|
|
PlutoService.AppLogInfo("Not one valid Medisoft Connection was found in the DataStore");
|
|
if (nMedisoftCount > 0)
|
|
PlutoService.AppLogInfo(String.Format("Loaded {0} Medisoft Shared Connection", nMedisoftCount));
|
|
|
|
return (bASuccessFullLytecReadOccured || bASuccessFullMedisoftReadOccured);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
PlutoService.AppLogError(String.Format("Error thrown reading DataStore: {0}", e.Message));
|
|
return false;
|
|
}
|
|
|
|
// No File exists, always return true
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Save user connnections to the DataStore any existing connections * Writes in all SQLServer & Medisoft Connectios to a file *
|
|
/// Note: UserApiKeys are never saved only the paths/credentials of the Connection
|
|
/// </summary>
|
|
internal static void SaveDataStore()
|
|
{
|
|
string ISFileContent = s_xmlserializer.WriteToString<DBCacheDataStore>(s_DBCredentialCacheDataStore);
|
|
ISFileContent = Encryption.EncryptText(ISFileContent);
|
|
s_ISReadWrite.WriteToIS(ISFileContent);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load all SQL Server User Credentials from a Shared Connection
|
|
/// </summary>
|
|
/// <param name="credential"></param>
|
|
internal static bool IterateSharedConnectionAndLoadUserApiKeyConnections(SQLServerCredential credential)
|
|
{
|
|
bool bSuccess = false;
|
|
if (credential != null)
|
|
{
|
|
DBRetVal retVal = LytecConnector.GetPracticeList(credential);
|
|
if (retVal.IsValid)
|
|
{
|
|
bSuccess = true;
|
|
foreach (DataRow row in retVal.GetDataTableRetVal().Rows)
|
|
{
|
|
string UserApiKey = DataRet.Retrieve(row["UserAPIKey"]);
|
|
if (!String.IsNullOrEmpty(UserApiKey))
|
|
{
|
|
SystemAccessVerifier verifier = new SystemAccessVerifier(UserApiKey);
|
|
if (!String.IsNullOrEmpty(verifier.SystemApiKey))
|
|
{
|
|
string UserDB = DataRet.Retrieve(row["Database Name"]);
|
|
string Password = ConnStr.RetrieveValue("PASSWORD", credential.ConnectionString);
|
|
|
|
// We must ensure uniqueness. Somehow both Medisoft & Lytec can create scenarios
|
|
// where they are not unique amongst themselves! strange, but true
|
|
// Don't overwrite connections that are already loaded (invalid state)
|
|
if (!_LytecConnetions.ContainsKey(verifier.SystemApiKey))
|
|
{
|
|
MSLSpecific.Logger.Info("Loading ApiKey Connection for DataBase:{0}", UserDB);
|
|
_LytecConnetions[verifier.SystemApiKey] = new SQLServerCredential(credential.Server, credential.Instance, UserDB, credential.User, Password);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bSuccess;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load all Advantage User Credentials from a Shared Connection
|
|
/// </summary>
|
|
/// <param name="credential"></param>
|
|
internal static bool IterateSharedConnectionAndLoadUserApiKeyConnections(AdvantageCredential credential)
|
|
{
|
|
bool bSuccess = false;
|
|
if (credential != null)
|
|
{
|
|
DBRetVal retVal = MedisoftConnector.GetPracticeList(credential);
|
|
if (retVal.IsValid)
|
|
{
|
|
bSuccess = true;
|
|
foreach (DataRow row in retVal.GetDataTableRetVal().Rows)
|
|
{
|
|
string UserApiKey = DataRet.Retrieve(row["UserAPIKey"]);
|
|
if (!String.IsNullOrEmpty(UserApiKey))
|
|
{
|
|
SystemAccessVerifier verifier = new SystemAccessVerifier(UserApiKey);
|
|
if (!String.IsNullOrEmpty(verifier.SystemApiKey))
|
|
{
|
|
string UserDB = DataRet.Retrieve(row["Data Path"]);
|
|
bool bIsRemote = (ConnStr.RetrieveValue("SERVERTYPE", credential.ConnectionString) == "REMOTE");
|
|
|
|
// We must ensure uniqueness. Somehow both Medisoft & Lytec can create scenarios
|
|
// where they are not unique amongst themselves! strange, but true
|
|
// Don't overwrite connections that are already loaded (invalid state)
|
|
if(!_MedisoftConnections.ContainsKey(verifier.SystemApiKey))
|
|
{
|
|
MSLSpecific.Logger.Info("Loading ApiKey Connection for DataBase:{0}", UserDB);
|
|
_MedisoftConnections[verifier.SystemApiKey] = new AdvantageCredential(UserDB + "\\mwddf.add", "user", "password", bIsRemote ? AdvantageCredential.ServerType.REMOTE : AdvantageCredential.ServerType.LOCAL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bSuccess;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Get Specific Connection
|
|
|
|
internal static SQLServerCredential GetSQLServerConnection(string SystemApiKey)
|
|
{
|
|
IConnectDb connection = GetConnectionForSystemApiKey(SystemApiKey);
|
|
if (connection is SQLServerCredential)
|
|
return (SQLServerCredential)connection;
|
|
else
|
|
return null;
|
|
}
|
|
|
|
internal static AdvantageCredential GetAdvantageConnection(string SystemApiKey)
|
|
{
|
|
IConnectDb connection = GetConnectionForSystemApiKey(SystemApiKey);
|
|
if (connection is AdvantageCredential)
|
|
return (AdvantageCredential)connection;
|
|
else
|
|
return null;
|
|
}
|
|
|
|
internal static AdvantageCredential GetAdvantageConnection(string SystemApiKey, string User)
|
|
{
|
|
AdvantageCredential credentialSystem = GetAdvantageConnection(SystemApiKey);
|
|
if (credentialSystem != null && !String.IsNullOrEmpty(User))
|
|
{
|
|
// First Retrieve the Password for that user
|
|
string SQL = String.Format("SELECT [Password] FROM [mwSEC] WHERE upper([Code]) = '{0}'", User.Replace("'", "''").ToUpper());
|
|
DB db = DB.Create(credentialSystem);
|
|
DBRetVal retVal = db.ExecuteScalar(SQL);
|
|
if (retVal.IsValid)
|
|
{
|
|
// Now Create a new Connection with the User and Password for Advantage
|
|
// ~Medisoft attaches 'mwmw' to the username for DB Connections
|
|
string pwd = retVal.GetScalarRetVal().Trim();
|
|
AdvantageCredential credentialUser = new AdvantageCredential(credentialSystem.DataSource, (User + "mwmw").ToUpper(), pwd.ToUpper(), credentialSystem.Type);
|
|
return credentialUser;
|
|
}
|
|
}
|
|
|
|
// Something Failed
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Get General Connection
|
|
|
|
/// <summary>
|
|
/// Looks up the System string in the Local User DB Connection cache and retrieves it,
|
|
/// if found. otherwise throws System not properly installed error for (release builds,
|
|
/// of this service). On Test machines it will always return something.
|
|
/// </summary>
|
|
/// <param name="SystemApiKey">SystemKey to check</param>
|
|
/// <returns></returns>
|
|
private static IConnectDb GetConnectionForSystemApiKey(string SystemApiKey)
|
|
{
|
|
if (!String.IsNullOrEmpty(SystemApiKey))
|
|
{
|
|
if (_LytecConnetions.ContainsKey(SystemApiKey))
|
|
return _LytecConnetions[SystemApiKey];
|
|
|
|
if (_MedisoftConnections.ContainsKey(SystemApiKey))
|
|
return _MedisoftConnections[SystemApiKey];
|
|
|
|
// * Setup Any Debug/Test Connections * Used Internally Only
|
|
if (IsMachine_Used_ForTesting)
|
|
{
|
|
if (ProductType.IsKeyLytec(SystemApiKey) && TestLytecUserDBCredential != null)
|
|
return TestLytecUserDBCredential;
|
|
else if (ProductType.IsKeyMedisoft(SystemApiKey) && TestMedisoftDBCredential != null)
|
|
return TestMedisoftDBCredential;
|
|
else
|
|
throw new Exception("Failed to retrieve DBCredential for ApiKey. Test Machine not properly configured.");
|
|
}
|
|
else
|
|
{
|
|
// not internally recognized, something went wrong or just Lytec / Medisoft
|
|
// calling in the wrong order * either way * nothing to return
|
|
// returning null will just mean something else blows up, so might as well
|
|
// throw an error and let the pluto api return false
|
|
PlutoService.AppLogError("Failed to retrieve ApiKey. System not properly configured.");
|
|
throw new Exception("Failed to retrieve ApiKey. System not properly configured.");
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
|
|
}
|