Skip to content

Instantly share code, notes, and snippets.

@MarkMpn
Created March 4, 2019 22:36
Show Gist options
  • Save MarkMpn/5b87ee3685e91f136858ba83844eef4c to your computer and use it in GitHub Desktop.
Save MarkMpn/5b87ee3685e91f136858ba83844eef4c to your computer and use it in GitHub Desktop.
XrmToolBox Connection Serialization
/// <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