XrmToolBox Connection Serialization
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Contains details of the web proxy to be used to connect to each CRM instance | |
/// </summary> | |
public class Proxy | |
{ | |
public bool UseCustomProxy { get; set; } | |
public bool UseInternetExplorerProxy { get; set; } | |
public string Address { get; set; } = ""; | |
public string Username { get; set; } = ""; | |
public string Password { get; set; } = ""; | |
public bool UseDefaultCredentials { get; set; } | |
public bool ByPassProxyOnLocal { get; set; } | |
} | |
/// <summary> | |
/// Contains details of the connection to an individual CRM instance | |
/// </summary> | |
public class CrmConnection | |
{ | |
/// <summary> | |
/// Not used directly, but needs to be present for XmlSerializer to work | |
/// </summary> | |
public CrmConnection() | |
{ | |
} | |
/// <summary> | |
/// Sets up most properties of the connection based on it's url | |
/// </summary> | |
/// <param name="uri">The URL of the CRM environment</param> | |
/// <param name="name">The name of the CRM environment to show to the user</param> | |
private CrmConnection(Uri uri, string name) | |
{ | |
ConnectionId = Guid.NewGuid(); | |
ConnectionName = name; | |
IsCustomAuth = false; | |
UseSsl = uri.Scheme == "https"; | |
ServerName = uri.Host; | |
ServerPort = uri.Port; | |
OriginalUrl = uri.ToString(); | |
Organization = GetOrgName(uri); | |
OrganizationUrlName = GetOrgUrlName(uri); | |
OrganizationFriendlyName = OrganizationUrlName; | |
OrganizationServiceUrl = FixUri(uri).ToString(); | |
OrganizationDataServiceUrl = new Uri(FixUri(uri), "OrganizationData.svc").ToString(); | |
Timeout = 1200000000; | |
WebApplicationUrl = new Uri(uri, "/").ToString(); | |
EnvironmentColor = "#FF00FF"; | |
EnvironmentTextColor = "#FFFFFF"; | |
} | |
/// <summary> | |
/// Creates a CRM connection that uses a refresh token or S2S authentication | |
/// </summary> | |
/// <param name="uri">The URL of the CRM environment</param> | |
/// <param name="name">The name of the CRM environment to show to the user</param> | |
/// <param name="refreshToken">The refresh token to use, or <c>null</c> if using S2S authentication</param> | |
/// <param name="replyUrl">The Reply URL configured on the Azure AD app</param> | |
/// <param name="appId">The unique identifier of the Azure AD app</param> | |
/// <param name="clientSecret">A secret key associated with the Azure AD app</param> | |
public CrmConnection(Uri uri, string name, string refreshToken, string replyUrl, Guid appId, string clientSecret) : this(uri, name) | |
{ | |
RefreshToken = con.RefreshToken; | |
ReplyUrl = replyUrl; | |
AzureAdAppId = appId; | |
S2SClientSecret = EncryptPassword(clientSecret); | |
AuthType = "OnlineFederation"; | |
UseOnline = true; | |
} | |
/// <summary> | |
/// Creates a CRM connection that uses password-based authentication | |
/// </summary> | |
/// <param name="uri">The URL of the CRM environment</param> | |
/// <param name="name">The name of the CRM environment to show to the user</param> | |
/// <param name="username">The username to use to authenticate as</param> | |
/// <param name="password">The password to use to authenticate with, or <c>null</c> if the user will need to enter the password manually</param> | |
public CrmConnection(Uri uri, string name, string username, string password) : this(uri) | |
{ | |
UserName = username; | |
UserPassword = String.IsNullOrEmpty(password) ? null : EncryptPassword(password); | |
SavePassword = !String.IsNullOrEmpty(UserPassword); | |
if (IsOnline(uri)) | |
{ | |
AuthType = "LiveId"; | |
UseOnline = true; | |
UseOsdp = true; | |
} | |
else if (IsOnPrem(uri)) | |
{ | |
AuthType = "ActiveDirectory"; | |
} | |
else | |
{ | |
AuthType = "Federation"; | |
UseIfd = true; | |
} | |
} | |
/// <summary> | |
/// Gets the name of the organization from the URL | |
/// </summary> | |
/// <param name="uri">The URL of the CRM environment</param> | |
/// <returns>The name of the organization</returns> | |
private string GetOrgUrlName(Uri uri) | |
{ | |
if (IsOnPrem(uri)) | |
return uri.AbsolutePath.Split('/')[1]; | |
return uri.Host.Split('.')[0]; | |
} | |
/// <summary> | |
/// Checks if a CRM environment is on-premise based on the URL | |
/// </summary> | |
/// <param name="uri">The URL of the CRM environment</param> | |
/// <returns><c>true</c> if the environment is on-premise, or <c>false</c> otherwise</returns> | |
/// <remarks> | |
/// IFD instances will return <c>false</c> | |
/// </remarks> | |
private bool IsOnPrem(Uri uri) | |
{ | |
return uri.AbsolutePath != "/" && uri.AbsolutePath != "/" + OrgServiceUri; | |
} | |
/// <summary> | |
/// Checks if a CRM evironment is online based on the URL | |
/// </summary> | |
/// <param name="uri">The URL of the CRM environment</param> | |
/// <returns><c>true</c> if the environment is online, or <c>false</c> otherwise</returns> | |
private bool IsOnline(Uri uri) | |
{ | |
return uri.Host.EndsWith(".dynamics.com"); | |
} | |
private const string OrgServiceUri = "XRMServices/2011/Organization.svc"; | |
/// <summary> | |
/// Gets the URL of the OrganizationService based on the URL of a CRM environment | |
/// </summary> | |
/// <param name="uri">The URL of a CRM environment</param> | |
/// <returns>The URL of the OrganizationService</returns> | |
private static Uri FixUri(Uri uri) | |
{ | |
if (uri.PathAndQuery.EndsWith(OrgServiceUri, StringComparison.OrdinalIgnoreCase)) | |
return uri; | |
return new Uri(uri, OrgServiceUri); | |
} | |
// Encryption parameters and code taken from | |
// https://github.com/MscrmTools/MscrmTools.Xrm.Connection/blob/master/McTools.Xrm.Connection/CryptoManager.cs | |
// https://github.com/MscrmTools/MscrmTools.Xrm.Connection/blob/master/McTools.Xrm.Connection/ConnectionManager.cs | |
internal const string CryptoHashAlgorythm = "SHA1"; | |
internal const string CryptoInitVector = "ahC3@bCa2Didfc3d"; | |
internal const int CryptoKeySize = 256; | |
internal const string CryptoPassPhrase = "MsCrmTools"; | |
internal const int CryptoPasswordIterations = 2; | |
internal const string CryptoSaltValue = "Tanguy 92*"; | |
private string EncryptPassword(string password) | |
{ | |
return Encrypt(password, CryptoPassPhrase, | |
CryptoSaltValue, | |
CryptoHashAlgorythm, | |
CryptoPasswordIterations, | |
CryptoInitVector, | |
CryptoKeySize); | |
} | |
private static string Encrypt(string plainText, | |
string passPhrase, | |
string saltValue, | |
string hashAlgorithm, | |
int passwordIterations, | |
string initVector, | |
int keySize) | |
{ | |
// Convert strings into byte arrays. | |
// Let us assume that strings only contain ASCII codes. | |
// If strings include Unicode characters, use Unicode, UTF7, or UTF8 | |
// encoding. | |
byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector); | |
byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue); | |
// Convert our plaintext into a byte array. | |
// Let us assume that plaintext contains UTF8-encoded characters. | |
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); | |
// First, we must create a password, from which the key will be derived. | |
// This password will be generated from the specified passphrase and | |
// salt value. The password will be created using the specified hash | |
// algorithm. Password creation can be done in several iterations. | |
PasswordDeriveBytes password = new PasswordDeriveBytes( | |
passPhrase, | |
saltValueBytes, | |
hashAlgorithm, | |
passwordIterations); | |
// Use the password to generate pseudo-random bytes for the encryption | |
// key. Specify the size of the key in bytes (instead of bits). | |
byte[] keyBytes = password.GetBytes(keySize / 8); | |
// Create uninitialized Rijndael encryption object. | |
RijndaelManaged symmetricKey = new RijndaelManaged(); | |
// It is reasonable to set encryption mode to Cipher Block Chaining | |
// (CBC). Use default options for other symmetric key parameters. | |
symmetricKey.Mode = CipherMode.CBC; | |
// Generate encryptor from the existing key bytes and initialization | |
// vector. Key size will be defined based on the number of the key | |
// bytes. | |
ICryptoTransform encryptor = symmetricKey.CreateEncryptor( | |
keyBytes, | |
initVectorBytes); | |
// Define memory stream which will be used to hold encrypted data. | |
MemoryStream memoryStream = new MemoryStream(); | |
// Define cryptographic stream (always use Write mode for encryption). | |
CryptoStream cryptoStream = new CryptoStream(memoryStream, | |
encryptor, | |
CryptoStreamMode.Write); | |
// Start encrypting. | |
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); | |
// Finish encrypting. | |
cryptoStream.FlushFinalBlock(); | |
// Convert our encrypted data from a memory stream into a byte array. | |
byte[] cipherTextBytes = memoryStream.ToArray(); | |
// Close both streams. | |
memoryStream.Close(); | |
cryptoStream.Close(); | |
// Convert encrypted data into a base64-encoded string. | |
string cipherText = Convert.ToBase64String(cipherTextBytes); | |
// Return encrypted string. | |
return cipherText; | |
} | |
public string AuthType { get; set; } | |
public Guid ConnectionId { get; set; } | |
public string ConnectionName { get; set; } | |
public string ConnectionString { get; set; } | |
public bool UseConnectionString { get; set; } | |
public bool IsCustomAuth { get; set; } | |
public bool UseMfa { get; set; } | |
public bool UseIfd { get; set; } | |
public bool UseOnline { get; set; } | |
public bool UseOsdp { get; set; } | |
public string UserDomain { get; set; } | |
public string UserName { get; set; } | |
public string UserPassword { get; set; } | |
public bool SavePassword { get; set; } | |
public bool UseSsl { get; set; } | |
public Guid AzureAdAppId { get; set; } | |
public string ReplyUrl { get; set; } | |
public string ServerName { get; set; } | |
public int ServerPort { get; set; } | |
public string OriginalUrl { get; set; } | |
public string Organization { get; set; } | |
public string OrganizationUrlName { get; set; } | |
public string OrganizationFriendlyName { get; set; } | |
public string OrganizationServiceUrl { get; set; } | |
public string OrganizationDataServiceUrl { get; set; } | |
public string OrganizationVersion { get; set; } | |
public string HomeRealmUrl { get; set; } | |
public int Timeout { get; set; } | |
public string WebApplicationUrl { get; set; } | |
public bool IsEnvironmentHighlightSet { get; set; } | |
public string EnvironmentText { get; set; } | |
public string EnvironmentColor { get; set; } | |
public string EnvironmentTextColor { get; set; } | |
public DateTime LastUsedOn { get; set; } | |
public string RefreshToken { get; set; } | |
public string S2SClientSecret { get; set; } | |
} | |
/// <summary> | |
/// Represents a list of connection details | |
/// </summary> | |
public class ConnectionDetails | |
{ | |
public Proxy Proxy { get; set; } | |
public bool UseMruDisplay { get; set; } | |
public string Name { get; set; } | |
[XmlElement("ConnectionDetail")] | |
public CrmConnection[] Connections { get; set; } | |
} | |
/// <summary> | |
/// Wrapper to store the connection details at the root of the XML document | |
/// </summary> | |
public class CrmConnections | |
{ | |
public ConnectionDetails Connections { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment