From a4443c1cfd799a06c829927cc0e00807d3162813 Mon Sep 17 00:00:00 2001 From: sfdrogojan <42441166+sfdrogojan@users.noreply.github.com> Date: Mon, 20 May 2019 14:04:12 +0300 Subject: [PATCH] Added OAuth2 authentication support Added OAuth2 authentication support. --- README.md | 96 ++++++++++++--------------- config.php.template | 3 + src/ET_Client.php | 154 +++++++++++++++++++++++++++++++++++--------- src/ET_Util.php | 2 +- 4 files changed, 170 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index 0902e5e..9fa6883 100755 --- a/README.md +++ b/README.md @@ -8,63 +8,49 @@ Salesforce Marketing Cloud Fuel SDK for PHP ## Overview ## The Fuel SDK for PHP provides easy access to Salesforce Marketic Cloud's Fuel API Family services, including a collection of REST and SOAP API. These APIs provide access to Salesforce Marketing Cloud (previously called ExactTarget) functionality via common collection types such as array/hash. -## New Features in Version 1.2.2 ## - -* Added support for in memory cache. - -## New Features in Version 1.2.1 ## - -* Updated robrichards/wse-php dependency version to 2.0.3 - -## New Features in Version 1.2.0 ## +## New Features in Version 1.3.0 ## + +* Added support for OAuth2 authentication - [More Details](https://developer.salesforce.com/docs/atlas.en-us.mc-app-development.meta/mc-app-development/integration-considerations.htm) +* To enable OAuth2 authentication, set `'useOAuth2Authentication' => true` in the config.php file or pass it in the `params` argument to the ET_Client constructor. +* Sample Config for OAuth2: +``` + 'appsignature' => 'none', + 'clientid' => '', + 'clientsecret' => '', + 'defaultwsdl' => 'https://webservice.exacttarget.com/etframework.wsdl', + 'xmlloc' => '/some/path/to/cache/ExactTargetWSDL.xml', + 'baseAuthUrl' => '', + 'baseSoapUrl' => '', + 'baseUrl' => '', + 'useOAuth2Authentication' => true, + 'accountId' => , + 'scope' => '' +``` +* Example passing config as a parameter to ET_Client constructor: +``` + $myclient = new ET_Client( + true, + true, + array( + 'appsignature' => 'none', + 'clientid' => '', + 'clientsecret' => '', + 'defaultwsdl' => 'https://webservice.exacttarget.com/etframework.wsdl', + 'xmlloc' => '/some/path/to/cache/ExactTargetWSDL.xml', + 'baseAuthUrl' => '', + 'baseSoapUrl' => '', + 'baseUrl' => '', + 'useOAuth2Authentication' => true, + 'accountId' => '', + 'scope' => '' + ) + ); +``` + +## Version 1.2.0 ## * Added support for your tenant’s endpoints - [More Details](https://developer.salesforce.com/docs/atlas.en-us.mc-apis.meta/mc-apis/your-subdomain-tenant-specific-endpoints.htm) -## New Features in Version 1.1.0 ## - -* **namespace :** namespace is introduced. - -* **newly supported objects:** - - Result Message - - Data Extract - - Triggered Send Summary - -* composer autoload issue fix - - - - - ## Requirements ## PHP Version >=5.6.24 @@ -89,7 +75,7 @@ The following code is an example of a minimal composer.json file:
 {
     "require": {
-        "salesforce-mc/fuel-sdk-php": "1.2.2"
+        "salesforce-mc/fuel-sdk-php": "1.3.0"
     }
 }
 
diff --git a/config.php.template b/config.php.template index e483f98..fd7ecdc 100755 --- a/config.php.template +++ b/config.php.template @@ -8,6 +8,9 @@ return array( 'baseUrl' => 'https://www.exacttargetapis.com', 'baseAuthUrl' => 'https://auth.exacttargetapis.com', 'baseSoapUrl' => 'https://webservice.exacttarget.com/Service.asmx', + 'useOAuth2Authentication' => false, + 'accountId' => , + 'scope' => '', 'proxyhost' => 'localhost', 'proxyport' => '8080', 'proxyusername' => '', diff --git a/src/ET_Client.php b/src/ET_Client.php index 30ce5e8..e902e1a 100755 --- a/src/ET_Client.php +++ b/src/ET_Client.php @@ -67,7 +67,7 @@ class ET_Client extends SoapClient private $wsdlLoc, $debugSOAP, $lastHTTPCode, $clientId, $clientSecret, $appsignature, $endpoint, - $tenantTokens, $tenantKey, $xmlLoc, $baseAuthUrl, $baseSoapUrl; + $tenantTokens, $tenantKey, $xmlLoc, $baseAuthUrl, $baseSoapUrl, $useOAuth2Authentication, $accountId, $scope; private $defaultBaseSoapUrl = 'https://webservice.exacttarget.com/Service.asmx'; @@ -110,7 +110,6 @@ function __construct($getWSDL = false, $debug = false, $params = null) $this->clientId = $config['clientid']; $this->clientSecret = $config['clientsecret']; $this->appsignature = $config['appsignature']; - $this->baseUrl = $config["baseUrl"]; $this->baseAuthUrl = $config["baseAuthUrl"]; if (array_key_exists('baseSoapUrl', $config)) { if (!empty($config["baseSoapUrl"])) { @@ -119,6 +118,20 @@ function __construct($getWSDL = false, $debug = false, $params = null) $this->baseSoapUrl = $this->defaultBaseSoapUrl; } } + if(array_key_exists('useOAuth2Authentication', $config)){$this->useOAuth2Authentication = $config['useOAuth2Authentication'];} + if(array_key_exists("baseUrl", $config)) + { + $this->baseUrl = $config["baseUrl"]; + } + else + { + if($this->isNullOrEmptyString($this->useOAuth2Authentication) || $this->useOAuth2Authentication === false) { + throw new Exception("baseUrl is null: Must be provided in config file when instantiating ET_Client"); + } + } + if (array_key_exists('accountId', $config)){$this->accountId = $config['accountId'];} + if (array_key_exists('scope', $config)){$this->scope = $config['scope'];} + if (array_key_exists('xmlloc', $config)){$this->xmlLoc = $config['xmlloc'];} if(array_key_exists('proxyhost', $config)){$this->proxyHost = $config['proxyhost'];} @@ -129,19 +142,19 @@ function __construct($getWSDL = false, $debug = false, $params = null) } if ($params) { - if ($params && array_key_exists('defaultwsdl', $params)){$this->wsdlLoc = $params['defaultwsdl'];} + if (array_key_exists('defaultwsdl', $params)){$this->wsdlLoc = $params['defaultwsdl'];} else {$this->wsdlLoc = "https://webservice.exacttarget.com/etframework.wsdl";} - if ($params && array_key_exists('clientid', $params)){$this->clientId = $params['clientid'];} - if ($params && array_key_exists('clientsecret', $params)){$this->clientSecret = $params['clientsecret'];} - if ($params && array_key_exists('appsignature', $params)){$this->appsignature = $params['appsignature'];} - if ($params && array_key_exists('xmlloc', $params)){$this->xmlLoc = $params['xmlloc'];} - - if ($params && array_key_exists('proxyhost', $params)){$this->proxyHost = $params['proxyhost'];} - if ($params && array_key_exists('proxyport', $params)){$this->proxyPort = $params['proxyport'];} - if ($params && array_key_exists('proxyusername', $params)) {$this->proxyUserName = $params['proxyusername'];} - if ($params && array_key_exists('proxypassword', $params)) {$this->proxyPassword = $params['proxypassword'];} - if ($params && array_key_exists('sslverifypeer', $params)) {$this->sslVerifyPeer = $params['sslverifypeer'];} - if ($params && array_key_exists('baseUrl', $params)) + if (array_key_exists('clientid', $params)){$this->clientId = $params['clientid'];} + if (array_key_exists('clientsecret', $params)){$this->clientSecret = $params['clientsecret'];} + if (array_key_exists('appsignature', $params)){$this->appsignature = $params['appsignature'];} + if (array_key_exists('xmlloc', $params)){$this->xmlLoc = $params['xmlloc'];} + + if (array_key_exists('proxyhost', $params)){$this->proxyHost = $params['proxyhost'];} + if (array_key_exists('proxyport', $params)){$this->proxyPort = $params['proxyport'];} + if (array_key_exists('proxyusername', $params)) {$this->proxyUserName = $params['proxyusername'];} + if (array_key_exists('proxypassword', $params)) {$this->proxyPassword = $params['proxypassword'];} + if (array_key_exists('sslverifypeer', $params)) {$this->sslVerifyPeer = $params['sslverifypeer'];} + if (array_key_exists('baseUrl', $params)) { $this->baseUrl = $params['baseUrl']; } @@ -149,7 +162,7 @@ function __construct($getWSDL = false, $debug = false, $params = null) { $this->baseUrl = "https://www.exacttargetapis.com"; } - if ($params && array_key_exists('baseAuthUrl', $params)) + if (array_key_exists('baseAuthUrl', $params)) { $this->baseAuthUrl = $params['baseAuthUrl']; } @@ -157,7 +170,7 @@ function __construct($getWSDL = false, $debug = false, $params = null) { $this->baseAuthUrl = "https://auth.exacttargetapis.com"; } - if ($params && array_key_exists('baseSoapUrl', $params)) + if (array_key_exists('baseSoapUrl', $params)) { if (!empty($params["baseSoapUrl"])) { $this->baseSoapUrl = $params['baseSoapUrl']; @@ -165,6 +178,18 @@ function __construct($getWSDL = false, $debug = false, $params = null) $this->baseSoapUrl = $this->defaultBaseSoapUrl; } } + if (array_key_exists('useOAuth2Authentication', $params)) + { + $this->useOAuth2Authentication = $params['useOAuth2Authentication']; + } + if (array_key_exists('accountId', $params)) + { + $this->accountId = $params['accountId']; + } + if (array_key_exists('scope', $params)) + { + $this->scope = $params['scope']; + } } $this->debugSOAP = $debug; @@ -246,7 +271,10 @@ function __construct($getWSDL = false, $debug = false, $params = null) */ function refreshToken($forceRefresh = false) { - + if ($this->useOAuth2Authentication === true){ + $this->refreshTokenWithOAuth2($forceRefresh); + return; + } if (property_exists($this, "sdl") && $this->sdl == 0){ parent::__construct($this->xmlLoc, array('trace'=>1, 'exceptions'=>0)); } @@ -292,6 +320,54 @@ function refreshToken($forceRefresh = false) throw new Exception('Unable to validate App Keys(ClientID/ClientSecret) provided.: '.$e->getMessage()); } } + + function refreshTokenWithOAuth2($forceRefresh = false) + { + if (property_exists($this, "sdl") && $this->sdl == 0){ + parent::__construct($this->xmlLoc, array('trace'=>1, 'exceptions'=>0)); + } + try { + $currentTime = new DateTime(); + if (is_null($this->getAuthTokenExpiration($this->tenantKey))){ + $timeDiff = 0; + } else { + $timeDiff = $currentTime->diff($this->getAuthTokenExpiration($this->tenantKey))->format('%i'); + $timeDiff = $timeDiff + (60 * $currentTime->diff($this->getAuthTokenExpiration($this->tenantKey))->format('%H')); + } + if (is_null($this->getAuthToken($this->tenantKey)) || ($timeDiff < 5) || $forceRefresh ){ + + $url = $this->baseAuthUrl."/v2/token"; + + $jsonRequest = new stdClass(); + $jsonRequest->client_id = $this->clientId; + $jsonRequest->client_secret = $this->clientSecret; + $jsonRequest->grant_type = "client_credentials"; + + if ($this->isNullOrEmptyString($this->accountId) == false){ + $jsonRequest->account_id = $this->accountId; + } + if ($this->isNullOrEmptyString($this->scope) == false){ + $jsonRequest->scope = $this->scope; + } + + $authResponse = ET_Util::restPost($url, json_encode($jsonRequest), $this); + $authObject = json_decode($authResponse->body); + + if ($authResponse && property_exists($authObject,"access_token")){ + $dv = new DateInterval('PT'.$authObject->expires_in.'S'); + $newexpTime = new DateTime(); + $this->setAuthToken($this->tenantKey, $authObject->access_token, $newexpTime->add($dv)); + $this->setInternalAuthToken($this->tenantKey, $authObject->access_token); + $this->baseUrl = $authObject->rest_instance_url; + $this->baseSoapUrl= $authObject->soap_instance_url."Service.asmx"; + } else { + throw new Exception('Unable to validate App Keys(ClientID/ClientSecret) provided, requestToken response:'.$authResponse->body ); + } + } + } catch (Exception $e) { + throw new Exception('Unable to validate App Keys(ClientID/ClientSecret) provided.: '.$e->getMessage()); + } + } /** * Returns the HTTP code return by the last SOAP/Rest call * @@ -379,12 +455,19 @@ function __doRequest($request, $location, $saction, $version, $one_way = 0) { $doc = new DOMDocument(); $doc->loadXML($request); - $objWSSE = new WSSESoap($doc); - $objWSSE->addUserToken("*", "*", FALSE); - $this->addOAuth($doc, $this->getInternalAuthToken($this->tenantKey)); - - $content = $objWSSE->saveXML(); - $content_length = strlen($content); + + if($this->useOAuth2Authentication === true){ + $this->addOAuth($doc, $this->getAuthToken($this->tenantKey)); + $content = $doc->saveXML(); + } + else{ + $objWSSE = new WSSESoap($doc); + $objWSSE->addUserToken("*", "*", FALSE); + $this->addOAuth($doc, $this->getInternalAuthToken($this->tenantKey)); + + $content = $objWSSE->saveXML(); + } + if ($this->debugSOAP){ error_log ('FuelSDK SOAP Request: '); error_log (str_replace($this->getInternalAuthToken($this->tenantKey),"REMOVED",$content)); @@ -432,7 +515,6 @@ public function addOAuth( $doc, $token) $soapPFX = $envelope->prefix; $SOAPXPath = new DOMXPath($doc); $SOAPXPath->registerNamespace('wssoap', $soapNS); - $SOAPXPath->registerNamespace('wswsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'); $headers = $SOAPXPath->query('//wssoap:Envelope/wssoap:Header'); $header = $headers->item(0); @@ -441,11 +523,17 @@ public function addOAuth( $doc, $token) $envelope->insertBefore($header, $envelope->firstChild); } - $authnode = $soapDoc->createElementNS('http://exacttarget.com', 'oAuth'); + if ($this->useOAuth2Authentication === true){ + $authnode = $soapDoc->createElementNS('http://exacttarget.com','fueloauth',$token); + } + else{ + $SOAPXPath->registerNamespace('wswsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'); + + $authnode = $soapDoc->createElementNS('http://exacttarget.com', 'oAuth'); + $oauthtoken = $soapDoc->createElementNS(null,'oAuthToken',$token); + $authnode->appendChild($oauthtoken); + } $header->appendChild($authnode); - - $oauthtoken = $soapDoc->createElementNS(null,'oAuthToken',$token); - $authnode->appendChild($oauthtoken); } /** @@ -790,7 +878,15 @@ function CreateContentAreas($arrayOfContentAreas) $sendResponse = $postC->post(); return $sendResponse; } - + + /** + * Function for basic field validation (present and neither empty nor only white space) + * + * @param string $str string to be validated + */ + function isNullOrEmptyString($str){ + return (!isset($str) || trim($str) === ''); + } } ?> diff --git a/src/ET_Util.php b/src/ET_Util.php index 6c18cf1..8514e43 100644 --- a/src/ET_Util.php +++ b/src/ET_Util.php @@ -288,7 +288,7 @@ public static function isAssoc($array) */ public static function getSDKVersion() { - return "FuelSDK-PHP-v1.2.2"; + return "FuelSDK-PHP-v1.3.0"; } /**