Skip to content

Commit

Permalink
Added feature to manage encrypted CRM connections in json config
Browse files Browse the repository at this point in the history
  • Loading branch information
WaelHamze committed Feb 2, 2019
1 parent 381c129 commit 8e9ce6e
Show file tree
Hide file tree
Showing 14 changed files with 705 additions and 4 deletions.
15 changes: 14 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -289,4 +289,17 @@ __pycache__/
*.odx.cs
*.xsd.cs

BuildTools/Lib/xRMCIFramework/9.0.0/
BuildTools/Lib/xRMCIFramework/9.0.0/
/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Xrm.Framework.CI.PowerShell.Cmdlets.dll
/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Xrm.Framework.CI.Common.dll
/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/System.Management.Automation.dll
/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Newtonsoft.Json.dll
/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Xrm.Tooling.Connector.dll
/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Xrm.Sdk.Workflow.dll
/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Xrm.Sdk.dll
/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Xrm.Sdk.Deployment.dll
/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Rest.ClientRuntime.dll
/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Management.Infrastructure.dll
/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll
/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.IdentityModel.Clients.ActiveDirectory.dll
/MSDYNV9/Xrm.Framework.CI/Xrm.Framework.CI.PowerShell.Scripts/Microsoft.Crm.Sdk.Proxy.dll
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Xrm.Framework.CI.Common.IntegrationTests.Logging;

namespace Xrm.Framework.CI.Common.IntegrationTests
{
[TestClass]
public class ConnectionConfigManagerTest
{
public TestContext TestContext
{
get;
set;
}

[TestMethod]
public void TestConnection()
{
string config = $"{TestContext.TestLogsDir}\\connections.json";

TestLogger logger = new TestLogger();
XrmEncryptionManager encryption = new XrmEncryptionManager(logger);
XrmConnectionConfigManager manager = new XrmConnectionConfigManager(logger, encryption, config);

string con1 = "AuthType=Office365;[email protected];Password=passwork;Url=https://name1.crmregion.dynamics.com";
string key1 = "crm1";

string con2 = "AuthType=Office365;[email protected];Password=passwork;Url=https://name2.crmregion.dynamics.com";
string key2 = "crm2";

Assert.AreEqual(manager.GetConnections().Count, 0);

manager.SetConnection(key1, con1);

Assert.AreEqual(con1, manager.GetConnection(key1));

manager.SetConnection(key2, con2);

Assert.AreEqual(con2, manager.GetConnection(key2));
Assert.AreEqual(manager.GetConnections().Count, 2);
Assert.AreEqual(manager.GetConnections()[0], key1);
Assert.AreEqual(manager.GetConnections()[1], key2);

manager.RemoveConnection(key1);

Assert.AreEqual(manager.GetConnections().Count, 1);
Assert.AreEqual(null, manager.GetConnection(key1));
Assert.AreEqual(con2, manager.GetConnection(key2));

manager.RemoveConnection(key2);

Assert.AreEqual(null, manager.GetConnection(key2));
Assert.AreEqual(manager.GetConnections().Count, 0);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Compile Include="ConnectionConfigManagerTest.cs" />
<Compile Include="ExportSolution.cs" />
<Compile Include="ManagePatches.cs" />
<Compile Include="ImportSolution.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@
<Compile Include="SolutionManagement\SolutionXml.cs" />
<Compile Include="XrmBase.cs" />
<Compile Include="XrmConnectionManager.cs" />
<Compile Include="XrmEncryptionManager.cs" />
<Compile Include="XrmConnectionConfigManager.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xrm.Framework.CI.Common.Logging;

namespace Xrm.Framework.CI.Common
{
public class XrmConnectionConfigManager
{
#region Properties

protected ILogger Logger
{
get;
set;
}

protected IXrmEncryption Encryption
{
get;
set;
}

public string ConfigPath
{
get;
private set;
}

internal XrmConnectionInfoList ConnectionList
{
get;
set;
}

#endregion

#region Constructors

public XrmConnectionConfigManager(ILogger logger, IXrmEncryption encryption, string configPath)
{
Logger = logger;
Encryption = encryption;

if (!String.IsNullOrEmpty(configPath))
{
ConfigPath = configPath;
}
else
{
ConfigPath = GetTempConfig();
}
Init();
}

#endregion

#region Methods

public void SetConnection(string key, string connectionString)
{
string encryptedValue = Encryption.Encrypt(connectionString);

XrmConnectionInfo info = FindConnection(key);

if (info != null)
{
info.Value = encryptedValue;
}
else
{
ConnectionList.Connections.Add(new XrmConnectionInfo
{
Key = key,
Value = encryptedValue
});
}

SaveList();
}

public void RemoveConnection(string key)
{
XrmConnectionInfo info = FindConnection(key);

if (info != null)
{
ConnectionList.Connections.Remove(info);
SaveList();
}
else
{
throw new Exception($"No connection found with key: {key}");
}
}

public string GetConnection(string key)
{
XrmConnectionInfo found = FindConnection(key);

if (found != null)
{
return Encryption.Decrypt(found.Value);
}
else
{
return null;
}
}

public List<string> GetConnections()
{
return (from c in ConnectionList.Connections
select c.Key).ToList<string>();

}

private string GetTempConfig()
{
string tempFolder = Path.GetTempPath();
string configFolder = Path.Combine(tempFolder, "xRMCIFramework");
if (!Directory.Exists(configFolder))
{
Directory.CreateDirectory(configFolder);
}
string configFile = "connections.json";

return Path.Combine(configFolder, configFile);
}

private void Init()
{
if (!File.Exists(ConfigPath))
{
ConnectionList = new XrmConnectionInfoList();
SaveList();
}
else
{
LoadList();
}
}

private XrmConnectionInfo FindConnection(string key)
{
var c = from cons in ConnectionList.Connections
where cons.Key == key
select cons;

List<XrmConnectionInfo> found = c.ToList<XrmConnectionInfo>();

if (found.Count > 1)
{
throw new Exception($"More than one connection found with key: {key}");
}
else if (found.Count == 1)
{
return found[0];
}
else
{
return null;
}
}

private void SaveList()
{
Serializers.SaveJson<XrmConnectionInfoList>(ConfigPath, ConnectionList);
}

private void LoadList()
{
ConnectionList = Serializers.ParseJson<XrmConnectionInfoList>(ConfigPath);
}

#endregion
}

public class XrmConnectionInfo
{
public string Key { get; set; }
public string Value { get; set; }
}

internal class XrmConnectionInfoList
{
public List<XrmConnectionInfo> Connections { get; set; }

public XrmConnectionInfoList()
{
Connections = new List<XrmConnectionInfo>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Xrm.Framework.CI.Common.Logging;

namespace Xrm.Framework.CI.Common
{
public interface IXrmEncryption
{
string Encrypt(string secret);
string Decrypt(string encryptedSecret);
}

public class XrmEncryptionManager : IXrmEncryption
{
#region Properties

protected ILogger Logger
{
get;
set;
}

#endregion

#region Constructors

public XrmEncryptionManager(ILogger logger )
{
Logger = logger;
}

#endregion

#region Methods

public string Encrypt(string secret)
{
Logger.LogVerbose("Ecrypting secret");

if (string.IsNullOrEmpty(secret))
{
throw new Exception("secret can't be empty");
}

byte[] bytes = Encoding.Default.GetBytes(secret);

byte[] encryptedBytes = ProtectedData.Protect(bytes, (byte[])null, DataProtectionScope.CurrentUser);

string encryptedSecret = BitConverter.ToString(encryptedBytes).Replace("-", "");

Logger.LogVerbose("Secret encrypted");

return encryptedSecret;
}

public string Decrypt(string encryptedSecret)
{
Logger.LogVerbose("Decrypting secret");

byte[] encryptedBytes = StringToByteArray(encryptedSecret);

byte[] bytes = ProtectedData.Unprotect(encryptedBytes, (byte[])null, DataProtectionScope.CurrentUser);

string secret = Encoding.Default.GetString(bytes);

Logger.LogVerbose("Decryptings secret");

return secret;
}

private static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}

#endregion
}
}
Loading

0 comments on commit 8e9ce6e

Please sign in to comment.