Summary
At the time I’m writing those lines, OroCRM is not yet officially released but Akeneo has just been released. Both applications use OroPlatform which has the capability to be accessible by external third parties via REST API. Some documentation is still missing about this part (thanks to alsma, a cookbook is available on github), so I think it will be very useful for those interesting by this topic.
The REST API will allow you to add (POST request), update (PUT request), delete (DELETE request) or get (GET request) information from or into the OroPlatform application. In this article, we will do an example by adding a contact to OroCRM via the API.
Authentication
To use the REST API of OroPlatform you will need to authenticate a user by using the WSSE Authentication protocol. To do the authentication, some information of an API user will be necessary:
- a username
- an api key
- the user salt (until the release v.1.0 will be published, after that it won’t be needed)
You will get the API Key from the User Management. When viewing the user profile, there is a button “Generate Key” click on it and you will get the API key:
To get the User Salt, you will have to find it directly from the database. Table ‘oro_user‘, column ‘salt‘. This information is true until the Oro Team release the version 1.0 because the salt should not be used from external applications…
The WSSE protocol ask you to send an Authorization WSSE header and a custom X-WSSE header. This last header should have keys generated for each request sent. You can generate those headers thanks to the command:
/var/www/orocrm$ php app/console oro:wsse:generate-header admin To use WSSE authentication add following headers to the request: Authorization: WSSE profile="UsernameToken" X-WSSE: UsernameToken Username="admin", PasswordDigest="U6wg4zpzy9C4goBdapNeS18Z/WQ=", Nonce="MTQ4YWVkNTg1NmRlMmRlYg==", Created="2014-03-22T23:53:39+01:00"
There are some problems here if you want to use those information:
- There is a lifetime of 5 minutes. It’s important to know it when you do tests or when the clock of your computer or virtual machine is not sync with the one of your third party script. I had the experience…
- You can use it only once. You have to generate for each new request
- Your third party script may not have access to the CLI of the OroPlatform. In this case you need to implement yourself the algorithm to generate those keys. You will find the solution below
Test WSSE Authentification
If you want to do a test to see if it works, you can do a test via curl in CLI
curl 'http://orocrm.diglin.local/app_dev.php/api/rest/latest/users/1' \ -H 'Authorization: WSSE profile="UsernameToken"' \ -H 'X-WSSE: UsernameToken Username="admin", PasswordDigest="U6wg4zpzy9C4goBdapNeS18Z/WQ=", Nonce="MTQ4YWVkNTg1NmRlMmRlYg==", Created="2014-03-22T23:53:39+01:00"'
The returned content is a json string
{"id":1,"username":"admin","email":"admin@localhost.com","namePrefix":null,"firstName":"admin","middleName":null,"lastName":"admin","nameSuffix":null,"birthday":null,"enabled":true,"lastLogin":"2014-03-21T16:48:59+00:00","loginCount":9,"createdAt":"2014-03-01T17:55:39+00:00","updatedAt":"2014-03-21T16:50:22+00:00","owner":{"id":1,"name":"Acme, General"},"roles":[{"id":3,"role":"ROLE_ADMINISTRATOR","label":"Administrator"}],"groups":[],"emails":[],"businessUnits":[{"id":1,"name":"Acme, General"}],"imagePath":null}
API Routes
Once you have all necessary information, you are ready to use the REST API. You can get a list of all API routes available for the application by using the command line from the application root folder. In the path /api/rest/{version}/, {version} can have the value ‘latest’ or ‘v1′.
php app/console router:debug | grep api
oro_api_delete_contact DELETE ANY ANY /api/rest/{version}/contacts/{id}.{_format} oro_api_get_contact GET ANY ANY /api/rest/{version}/contacts/{id}.{_format} oro_api_get_contacts GET ANY ANY /api/rest/{version}/contacts.{_format} oro_api_post_contact POST ANY ANY /api/rest/{version}/contact.{_format} oro_api_put_contact PUT ANY ANY /api/rest/{version}/contacts/{id}.{_format} oro_api_delete_contactgroup DELETE ANY ANY /api/rest/{version}/contactgroups/{id}.{_format} oro_api_get_contactgroup GET ANY ANY /api/rest/{version}/contactgroups/{id}.{_format} oro_api_get_contactgroups GET ANY ANY /api/rest/{version}/contactgroups.{_format} oro_api_post_contactgroup POST ANY ANY /api/rest/{version}/contactgroup.{_format} oro_api_put_contactgroup PUT ANY ANY /api/rest/{version}/contactgroups/{id}.{_format} oro_api_delete_contact_address DELETE ANY ANY /api/rest/{version}/contacts/{contactId}/addresses/{addressId}.{_format} oro_api_get_contact_address GET ANY ANY /api/rest/{version}/contacts/{contactId}/addresses/{addressId}.{_format} oro_api_get_contact_address_by_type GET ANY ANY /api/rest/{version}/contacts/{contactId}/addresses/{typeName}/by/type.{_format} oro_api_get_contact_address_primary GET ANY ANY /api/rest/{version}/contacts/{contactId}/address/primary.{_format} oro_api_get_contact_addresses GET ANY ANY /api/rest/{version}/contacts/{contactId}/addresses.{_format} oro_api_get_contact_phone_primary GET ANY ANY /api/rest/{version}/contacts/{contactId}/phone/primary.{_format} oro_api_get_contact_phones GET ANY ANY /api/rest/{version}/contacts/{contactId}/phones.{_format}
To add a contact into OroCRM for example, you may use the url http://www.mydomain.tld/api/rest/latest/contact/
Add a contact via the API to OroCRM
Before to add a contact, you need to know the structure of the data to send via POST request. By getting a contact information, you will have the structure to send to OroCRM. Here is an example:
curl 'http://orocrm.diglin.local/app_dev.php/api/rest/latest/contacts/1' \ >-H 'Authorization: WSSE profile="UsernameToken"' \ >-H 'X-WSSE: UsernameToken Username="admin", PasswordDigest="sgFfs7dM0kKL8TbaUxpKTvvywSU=", Nonce="MDY3ODk4MzBmNjVmOTRhMA==", Created="2014-03-23T00:10:59+01:00"' {"id":1,"namePrefix":null,"firstName":"Ramona","middleName":null,"lastName":"Venters","nameSuffix":null,"gender":null,"birthday":null,"description":"Sure Save","jobTitle":null,"fax":null,"skype":null,"twitter":null,"facebook":null,"googlePlus":null,"linkedIn":null,"createdAt":"2014-03-01T17:56:01+00:00","updatedAt":"2014-03-01T17:56:01+00:00","email":null,"source":null,"method":null,"owner":15,"assignedTo":null,"reportsTo":null,"emails":{},"phones":{},"addresses":[{"primary":true,"id":1,"label":"Primary Address","street":"873 John Avenue","street2":null,"city":"Jackson","postalCode":"49201","organization":null,"regionText":null,"namePrefix":null,"firstName":"Ramona","middleName":null,"lastName":"Venters","nameSuffix":null,"types":[],"country":"United States","region":"Michigan"}],"groups":[],"accounts":[],"createdBy":15,"updatedBy":15}
Now if you want to create a new contact, you can send POST data via curl with this example string
curl -X POST 'http://orocrm.diglin.local/app_dev.php/api/rest/latest/contact' -d "contact[firstName]=Sylvain;contact[lastName]=Rayé;contact[jobTitle]=CTO;"
The same example with a PHP script:
$username = 'admin'; $apiUserKey = ''; $userSalt = ''; // Will be removed in version 1.0 of OroCRM $url = 'http://orocrm.diglin.local/app_dev.php/api/rest/latest/contact'; $data = array( 'contact[firstName]' => 'Sylvain', 'contact[lastName]' => 'Rayé', 'contact[jobTitle]' => 'Ingénieur' ); $oroWsse = new OroWsseAuthentification($username, $apiUserKey, $userSalt); $ch = curl_init(); if (!empty($data)) { $array = array( CURLOPT_POST => true, CURLOPT_POSTFIELDS => $data ); curl_setopt_array($ch, $array); } $headers = $oroWsse->getHeaders(); print_r($headers); $array = array( CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_HEADER => 0, CURLOPT_FAILONERROR => true, CURLOPT_URL => $url ); curl_setopt_array($ch, $array); $result = curl_exec($ch); if ( $result === false) { echo curl_error($ch); } else { echo ($result) . "\n"; } curl_close($ch);
PHP Class for OroWsseAuthentification
Because you need to generate the WSSE headers for each request done to the OroPlatform API, you will be more than happy to have the algorithm generated directly from your application. You will find below the class which will help you to generate these headers for you. The algorithm is taken from OroPlatform in the class ‘Oro\Bundle\UserBundle\Command\GenerateWSSEHeaderCommand’. In future versions of OroPlatform, it can still change.
class OroWsseAuthentification { protected $_username; protected $_apiKey; protected $_userSalt; /** * @param $username * @param $apiUserKey * @param string $userSalt */ public function __construct ($username, $apiUserKey, $userSalt = '') { $this->_username = $username; $this->_apiKey = $apiUserKey; $this->_userSalt = $userSalt; // deprecated in OroCRM v1.0 } /** * @param $raw * @param $salt * @return string */ private function _encodePassword($raw, $salt) { $salted = $this->_mergePasswordAndSalt($raw, $salt); $digest = hash('sha1', $salted, true); return base64_encode($digest); } /** * @param $password * @param $salt * @return string * @throws InvalidArgumentException */ private function _mergePasswordAndSalt($password, $salt) { if (empty($salt)) { return $password; } if (false !== strrpos($salt, '{') || false !== strrpos($salt, '}')) { throw new \InvalidArgumentException('Cannot use { or } in salt.'); } return $password.'{'.$salt.'}'; } /** * @return array */ public function getHeaders () { $prefix = gethostname(); $created = date('c'); $nonce = base64_encode(substr(md5(uniqid($prefix . '_', true)), 0, 16)); $passwordDigest = $this->_encodePassword(base64_decode($nonce) . $created . $this->_apiKey, $this->_userSalt); $wsseProfile = sprintf( 'X-WSSE: UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"', $this->_username, $passwordDigest, $nonce, $created ); return array( 'Authorization: WSSE profile="UsernameToken"', $wsseProfile ); } }
Conclusion
I hope that it was for you helpful, feel free to add some comments and feedbacks. It’s definitely a good start for any further development. The complete code of this article is available at https://gist.github.com/diglin/9716109. Thanks to the comment of alsma, you will find also the official documentation on github: https://github.com/orocrm/documentation/blob/master/cookbook/how_to_use_wsse_authentication.rst