Image may be NSFW.
Clik here to view.
Microsoft Dynamics CRM 2011 PHP CMS Website Integration
I was asked how easy it would be to integrate CRM with an Open source CMS system. Having a look online I realised that many people have asked this before, however there is no solid information on how to achieve this.
I thought I would have a go!
The following things had to be taken into account
Application platforms:
MS CRM runs on a windows server based (IIS) platform and the CMS system is running on a Linux based Apache server.
Development Languages:
MS CRM uses the .Net language and the CMS system uses PHP.
Location:
MS CRM is deployed on the systems internal network and the CMS is hosted with a shared hosting provider.
Security:
The transmissions will include user credentials, MSCRM 2011 requires HTTPS when using its SOAP services, The data being transferred should be encrypted for extra security.
Solution Overview
For this solution, I have nominated Joomla 1.5 as the Open source CMS system, I am a huge fan of Joomla and I think it is used widely enough to be of any use to readers of this article.
The objective of this article is to demonstrate how you can achieve the following.
• Use PHP SOAP messages to access Dynamics CRM 2011 over the internet securely
• Encrypt and pass data between PHP and .Net
• Call a Web Service from a Joomla Plugin
• Impersonate a Joomla User account with CRM stored Contact credentials.
So we don’t end up creating a user for every contact we have in MSCRM, we will be using one Joomla user account record. This isn’t a issue as Joomla allows multiple logins for any one user. The benefit of this is that we can set the security roles for that account in Joomla.
The image below shows the Joomla User account will be “CRMWEBUSER” that will be used to authenticate a correct MSCRM contact against.
Image may be NSFW.
Clik here to view.
Image may be NSFW.
Clik here to view.
The image above shows the MSCRM contact record with the following added key fields populated
new_username: This will store a contacts username (plain text)
new_password: This will store the contacts password (plain text)
The above fields will be used to query the correct contact record that matches the username and password the user has typed into the Joomla login page.
Joomla Authentcation Plugin (CrmAuth.php)
{codecitation style="brush: php;"}
<?php
// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die();
//This is required for calling CRM via SOAP
require_once('nusoap.php');
jimport('joomla.event.plugin');
class plgAuthenticationCrmAuth extends JPlugin
{
function plgAuthenticationCrmAuth(& $subject) {
parent::__construct($subject);
}
function onAuthenticate( $credentials, $options, &$response )
{
$main = $this->getCRMAuthentication($credentials['username'],$credentials['password']);
if (is_array($main))
{
$db =& JFactory::getDBO();
$query = 'SELECT `id`'
. ' FROM #__users'
. ' WHERE username=' . $db->quote($this->decrypt($main[0]));
$db->setQuery( $query );
$result = $db->loadResult();
if ($result)
{
$response->username = $this->decrypt($main[0]);
$response->email = $this->decrypt($main[2]);
$response->fullname = $this->decrypt($main[3]);
$response->status = JAUTHENTICATE_STATUS_SUCCESS;
}
else
{
$response->error_message = 'CRM Authentication has failed. User does not exist';
$response->status = JAUTHENTICATE_STATUS_FAILURE;
}
}
}
$mynamespace = "http://mscrmtech.com";
//Retrieve Array
if (isset($result['AuthenticateCrmWebUserResult']))
{
$main = $result['AuthenticateCrmWebUserResult'];
$authToken = $main['string'];
return $authToken;
}
else
{
$response->error_message = 'CRM Authentication has failed. User does not exist';
$response->status = JAUTHENTICATE_STATUS_FAILURE;
}
}
function encrypt($string){
//Key
$key = "xxxxxxxx";
//Encryption
$cipher_alg = MCRYPT_TRIPLEDES;$iv = mcrypt_create_iv(mcrypt_get_iv_size($cipher_alg,MCRYPT_MODE_ECB), MCRYPT_RAND);
$encrypted_string = mcrypt_encrypt($cipher_alg, $key, $string, MCRYPT_MODE_ECB, $iv);
return base64_encode($encrypted_string);
return $encrypted_string;
}
function decrypt($string){
$string = base64_decode($string);
//key
$key = "xxxxxxxx";
$cipher_alg = MCRYPT_TRIPLEDES;
$iv = mcrypt_create_iv(mcrypt_get_iv_size($cipher_alg,MCRYPT_MODE_ECB), MCRYPT_RAND);
$decrypted_string = mcrypt_decrypt($cipher_alg, $key, $string, MCRYPT_MODE_ECB, $iv);
return trim($decrypted_string);
}
}
?>
{/codecitation}
MSCRMTECH Custom Webservice (JoomlaCRMIntegration.asmx.cs)
{codecitation style="brush: C#;"}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Text;
using System.ServiceModel.Description;
using System.Security.Cryptography;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk;
using System.Configuration;
using System.Net;
using System.Xml;
using System.ServiceModel;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Messages;
using System.Web.Services.Protocols;
[WebService(Namespace = "http://mscrmtech.com/")]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
//[System.Web.Script.Services.ScriptService]
public class JoomlaCRMIntegration : System.Web.Services.WebService
{
string[] authToken;
private System.Text.Encoding encoding;
[WebMethod]
public string[] AuthenticateCrmWebUser(string username, string password)
{
//Decrypt Username and Password and remove padding.
String dCryptedUsername = DecryptString(username).Replace("\0", "");
String dCryptedPassword = DecryptString(password).Replace("\0", "");
try
{
//Look for a contact that has the same combination of username and password
Entity con = retrieveUser(dCryptedUsername, dCryptedPassword);
if (con != null)
{
//Generate new Array to send to joomla containing extra information about the website user.
authToken = new string[10];
//Encrypt the username assigned in joomla for all the crm users (CRMWEBUSER is used in this example).
authToken[0] = Encrypt3DES("crmwebuser");
//Encrypt Email Address
authToken[2] = Encrypt3DES(con["emailaddress1"].ToString());
//Encrypt Full Name
authToken[3] = Encrypt3DES(con["fullname"].ToString());
//Return Array with encrypted strings
return authToken;
}
else
{
return null;
}
}
catch (SoapException ex)
{
throw (ex);
}
}
public System.Text.Encoding Encoding
{
get
{
if( encoding == null )
{
encoding = System.Text.Encoding.UTF8;
}
return encoding;
}
set
{
encoding = value;
}
}
//Encryption Methods...........................................
// Encrypt String with 3DES
public string Encrypt3DES( string strString )
{
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
DES.Key = Encoding.GetBytes( this.Key );
DES.Mode = CipherMode.ECB;
DES.Padding = PaddingMode.Zeros;
ICryptoTransform DESEncrypt = DES.CreateEncryptor();
byte[] Buffer = encoding.GetBytes(strString);
return Convert.ToBase64String(DESEncrypt.TransformFinalBlock(Buffer, 0, Buffer.Length));
}
// Decrypt String with 3DES
public string Decrypt3DES( string strString )
{
DESCryptoServiceProvider DES = new DESCryptoServiceProvider ();
DES.Key = Encoding.UTF8.GetBytes( this.Key );
DES.Mode = CipherMode.ECB;
DES.Padding = PaddingMode.Zeros;
ICryptoTransform DESDecrypt = DES.CreateDecryptor();
byte[] Buffer = Convert.FromBase64String(strString);
return UTF8Encoding.UTF8.GetString( DESDecrypt.TransformFinalBlock(Buffer, 0, Buffer.Length) );
}
public string DecryptString(string name)
{
return Decrypt3DES(name);
}
//Key for encryption (has to match up with the key on the PHP file)
public string Key
{
get
{
return "xxxxxxxx";
}
}
//CRM 2011 Methods................................................
//CRM 2011 Service Methods
public OrganizationServiceProxy getCRMService(string orgUri)
{
ClientCredentials Credentials = new ClientCredentials();
//Credentials.Windows.ClientCredential = new System.Net.NetworkCredential("Administrator","Pass@word123","mscrm");
Uri OrganizationUri = new Uri(orgUri);
Uri HomeRealmUri = null;
OrganizationServiceProxy serviceProxy = null;
try
{
serviceProxy = new OrganizationServiceProxy(OrganizationUri, HomeRealmUri, Credentials, null);
}
catch (Exception ex)
{
throw (ex);
}
serviceProxy = getCRMService("http://crm2k11server:5555/CRM5/XRMServices/2011/Organization.svc");
return serviceProxy;
}
using (OrganizationServiceProxy serviceProxy = getCRMService("http://crm2k11server:5555/CRM5/XRMServices/2011/Organization.svc"))
ConditionExpression condition1 = new ConditionExpression();
condition1.AttributeName = "new_username";
condition1.Operator = ConditionOperator.Equal;
condition1.Values.Add(user.Trim());
ConditionExpression condition2 = new ConditionExpression();
condition2.AttributeName = "new_password";
condition2.Operator = ConditionOperator.Equal;
condition2.Values.Add(pass.Trim());
FilterExpression filter1 = new FilterExpression();
filter1.FilterOperator = LogicalOperator.And;
filter1.Conditions.Add(condition1);
filter1.Conditions.Add(condition2);
QueryExpression query = new QueryExpression("contact");
query.ColumnSet.AddColumns("contactid", "fullname", "emailaddress1");
query.Criteria.AddFilter(filter1);
EntityCollection retrieved = serviceProxy.RetrieveMultiple(query);
if (retrieved.Entities.Count != 0)
{
Entity con = new Entity("contact");
con = retrieved.Entities[0];
return con;
}
return null;
}
}
catch (SoapException ex)
{
throw (ex);
}
}
}
}
{/codecitation}
MSCRM 2011 / Joomla Authentication Process
Process:
- User enters username and password into Joomla login page.
- The custom Joomla authentication plugin captures the credentials that have been entered and encrypts them using Triple DES encryption (using a shared Key with the Web service encryption/decryption methods)
- The plugin passes the encrypted username and password to the AuthenticateCRMWebUser method of the MSCRMTECH Web Service.
- The MSCRMTECH Web Service decrypts the username and password strings and uses them to generate a query (QueryExpression) against MSCRM looking for a contact with matching username and password fields.
- If a contact has been retrieved, the MSCRMTECH Web Service encrypts the retrieved contact details (full name, email address) and adds them to an array (authentication token).
- MSCRMTECH Web Service sends the array (authentication token) to the Joomla authentication plugin.
- The Joomla authentication plugin check’s the token isn’t empty (null) – if the token isn’t empty, then the plugin builds up a JAuthenticationResponse providing the contact details. These details (email address, fullname etc) can be used in the CMS system.
- User logs in
Installing the Joomla Authentication Plugin
Access the administrator area of the joomla website
Image may be NSFW.
Clik here to view.
Upload the plugin via the extension manager (the file Crm 2011 Authentication Plugin.zip) is included in the solution available to download)
Image may be NSFW.
Clik here to view.
Once the plugin is installed, you need to enable it and add the location of the MSCRM custom web service WSDL
Image may be NSFW.
Clik here to view.
You should get confirmation that the plugin has been saved correctly
Image may be NSFW.
Clik here to view.
MSCRMTECH Custom Webservice
Once installed (and deployed) you should see the following public web method
Image may be NSFW.
Clik here to view.
The web method takes two parameters (username and password) that have to be encrypted using 3DES
Image may be NSFW.
Clik here to view.
The WSDL URL is the location string that needs to be added to the Joomla Authentication Plugin
Image may be NSFW.
Clik here to view.
Possible uses
This solution is far from perfect. However I like to think that the concepts of this solution can help you tackle your CRM/CMS/PHP integration projects.
The Web Service could effectively be used to create contacts based on the CMS trigger for new users.
The AuthenticationToken (String Array!) could be serialised properly over the internet.
More details can be passed over to the CMS system, due to the time constraints I set myself for this article I wasn’t able to explore how to get Joomla to display the full name of the contact record other than the authenticated Joomla user (CRMWEBUSER)
The Web service could be modified to impersonate a different users based on a Picklist field in the contact record (e.g. web role Picklist on the contact relates to a different Joomla user that has different security levels)
All the source code is available for this project to get it working.
I’ve tested this using a virtual windows environment and installing WAMP to it to emulate a Apache/MySQL environment.
I hope this is useful and I’m more than happy to take any questions on this. I would love to hear your thoughts on this and whether or not you found it useful.
Enjoy
Jonathan