forked from LiveCarta/PayPal-PHP-SDK
Added Encryption to Caching
- Access Token is encrypted with Secret Key, and stored in file - Code to auto-update existing cache files to use encrypted token - Tests fixes accordingly
This commit is contained in:
@@ -11,6 +11,7 @@ use PayPal\Exception\PayPalConfigurationException;
|
|||||||
use PayPal\Exception\PayPalConnectionException;
|
use PayPal\Exception\PayPalConnectionException;
|
||||||
use PayPal\Handler\IPayPalHandler;
|
use PayPal\Handler\IPayPalHandler;
|
||||||
use PayPal\Rest\ApiContext;
|
use PayPal\Rest\ApiContext;
|
||||||
|
use PayPal\Security\Cipher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class OAuthTokenCredential
|
* Class OAuthTokenCredential
|
||||||
@@ -74,6 +75,13 @@ class OAuthTokenCredential extends PayPalResourceModel
|
|||||||
*/
|
*/
|
||||||
private $tokenCreateTime;
|
private $tokenCreateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of cipher used to encrypt/decrypt data while storing in cache.
|
||||||
|
*
|
||||||
|
* @var Cipher
|
||||||
|
*/
|
||||||
|
private $cipher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct
|
* Construct
|
||||||
*
|
*
|
||||||
@@ -84,6 +92,7 @@ class OAuthTokenCredential extends PayPalResourceModel
|
|||||||
{
|
{
|
||||||
$this->clientId = $clientId;
|
$this->clientId = $clientId;
|
||||||
$this->clientSecret = $clientSecret;
|
$this->clientSecret = $clientSecret;
|
||||||
|
$this->cipher = new Cipher($this->clientSecret);
|
||||||
$this->logger = PayPalLoggingManager::getInstance(__CLASS__);
|
$this->logger = PayPalLoggingManager::getInstance(__CLASS__);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,9 +129,20 @@ class OAuthTokenCredential extends PayPalResourceModel
|
|||||||
$token = AuthorizationCache::pull($config, $this->clientId);
|
$token = AuthorizationCache::pull($config, $this->clientId);
|
||||||
if ($token) {
|
if ($token) {
|
||||||
// We found it
|
// We found it
|
||||||
$this->accessToken = $token['accessToken'];
|
// This code block is for backward compatibility only.
|
||||||
|
if (array_key_exists('accessToken', $token)) {
|
||||||
|
$this->accessToken = $token['accessToken'];
|
||||||
|
}
|
||||||
|
|
||||||
$this->tokenCreateTime = $token['tokenCreateTime'];
|
$this->tokenCreateTime = $token['tokenCreateTime'];
|
||||||
$this->tokenExpiresIn = $token['tokenExpiresIn'];
|
$this->tokenExpiresIn = $token['tokenExpiresIn'];
|
||||||
|
|
||||||
|
// Case where we have an old unencrypted cache file
|
||||||
|
if (!array_key_exists('accessTokenEncrypted', $token)) {
|
||||||
|
AuthorizationCache::push($config, $this->clientId, $this->encrypt($this->accessToken), $this->tokenCreateTime, $this->tokenExpiresIn);
|
||||||
|
} else {
|
||||||
|
$this->accessToken = $this->decrypt($token['accessTokenEncrypted']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if Access Token is not null and has not expired.
|
// Check if Access Token is not null and has not expired.
|
||||||
@@ -137,11 +157,12 @@ class OAuthTokenCredential extends PayPalResourceModel
|
|||||||
$this->accessToken = null;
|
$this->accessToken = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// If accessToken is Null, obtain a new token
|
// If accessToken is Null, obtain a new token
|
||||||
if ($this->accessToken == null) {
|
if ($this->accessToken == null) {
|
||||||
// Get a new one by making calls to API
|
// Get a new one by making calls to API
|
||||||
$this->updateAccessToken($config);
|
$this->updateAccessToken($config);
|
||||||
AuthorizationCache::push($config, $this->clientId, $this->accessToken, $this->tokenCreateTime, $this->tokenExpiresIn);
|
AuthorizationCache::push($config, $this->clientId, $this->encrypt($this->accessToken), $this->tokenCreateTime, $this->tokenExpiresIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->accessToken;
|
return $this->accessToken;
|
||||||
@@ -231,6 +252,7 @@ class OAuthTokenCredential extends PayPalResourceModel
|
|||||||
*
|
*
|
||||||
* @param array $config
|
* @param array $config
|
||||||
* @return null
|
* @return null
|
||||||
|
* @throws PayPalConnectionException
|
||||||
*/
|
*/
|
||||||
private function generateAccessToken($config, $refreshToken = null)
|
private function generateAccessToken($config, $refreshToken = null)
|
||||||
{
|
{
|
||||||
@@ -259,4 +281,26 @@ class OAuthTokenCredential extends PayPalResourceModel
|
|||||||
|
|
||||||
return $this->accessToken;
|
return $this->accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to encrypt data using clientSecret as key
|
||||||
|
*
|
||||||
|
* @param $data
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function encrypt($data)
|
||||||
|
{
|
||||||
|
return $this->cipher->encrypt($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to decrypt data using clientSecret as key
|
||||||
|
*
|
||||||
|
* @param $data
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function decrypt($data)
|
||||||
|
{
|
||||||
|
return $this->cipher->decrypt($data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ abstract class AuthorizationCache
|
|||||||
* @param $accessToken
|
* @param $accessToken
|
||||||
* @param $tokenCreateTime
|
* @param $tokenCreateTime
|
||||||
* @param $tokenExpiresIn
|
* @param $tokenExpiresIn
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function push($config = null, $clientId, $accessToken, $tokenCreateTime, $tokenExpiresIn)
|
public static function push($config = null, $clientId, $accessToken, $tokenCreateTime, $tokenExpiresIn)
|
||||||
{
|
{
|
||||||
@@ -58,7 +59,7 @@ abstract class AuthorizationCache
|
|||||||
$cachePath = self::cachePath($config);
|
$cachePath = self::cachePath($config);
|
||||||
if (!is_dir(dirname($cachePath))) {
|
if (!is_dir(dirname($cachePath))) {
|
||||||
if (mkdir(dirname($cachePath), 0755, true) == false) {
|
if (mkdir(dirname($cachePath), 0755, true) == false) {
|
||||||
return;
|
throw new \Exception("Failed to create directory at $cachePath");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,12 +69,14 @@ abstract class AuthorizationCache
|
|||||||
if (is_array($tokens)) {
|
if (is_array($tokens)) {
|
||||||
$tokens[$clientId] = array(
|
$tokens[$clientId] = array(
|
||||||
'clientId' => $clientId,
|
'clientId' => $clientId,
|
||||||
'accessToken' => $accessToken,
|
'accessTokenEncrypted' => $accessToken,
|
||||||
'tokenCreateTime' => $tokenCreateTime,
|
'tokenCreateTime' => $tokenCreateTime,
|
||||||
'tokenExpiresIn' => $tokenExpiresIn
|
'tokenExpiresIn' => $tokenExpiresIn
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
file_put_contents($cachePath, json_encode($tokens));
|
if(!file_put_contents($cachePath, json_encode($tokens))) {
|
||||||
|
throw new \Exception("Failed to write cache");
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
57
lib/PayPal/Security/Cipher.php
Normal file
57
lib/PayPal/Security/Cipher.php
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PayPal\Security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Cipher
|
||||||
|
*
|
||||||
|
* Helper class to encrypt/decrypt data with secret key
|
||||||
|
*
|
||||||
|
* @package PayPal\Security
|
||||||
|
*/
|
||||||
|
class Cipher
|
||||||
|
{
|
||||||
|
private $secretKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixed IV Size
|
||||||
|
*/
|
||||||
|
const IV_SIZE = 16;
|
||||||
|
|
||||||
|
function __construct($secretKey)
|
||||||
|
{
|
||||||
|
$this->$secretKey = $secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts the input text using the cipher key
|
||||||
|
*
|
||||||
|
* @param $input
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function encrypt($input)
|
||||||
|
{
|
||||||
|
// Create a random IV. Not using mcrypt to generate one, as to not have a dependency on it.
|
||||||
|
$iv = substr(uniqid("", true), 0, Cipher::IV_SIZE);
|
||||||
|
// Encrypt the data
|
||||||
|
$encrypted = openssl_encrypt($input, "AES-256-CBC", $this->secretKey, 0, $iv);
|
||||||
|
// Encode the data with IV as prefix
|
||||||
|
return base64_encode($iv . $encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts the input text from the cipher key
|
||||||
|
*
|
||||||
|
* @param $input
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function decrypt($input)
|
||||||
|
{
|
||||||
|
// Decode the IV + data
|
||||||
|
$input = base64_decode($input);
|
||||||
|
// Remove the IV
|
||||||
|
$iv = substr($input, 0, Cipher::IV_SIZE);
|
||||||
|
// Return Decrypted Data
|
||||||
|
return openssl_decrypt(substr($input, Cipher::IV_SIZE), "AES-256-CBC", $this->secretKey, 0, $iv);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,6 @@ $invoice = new Invoice();
|
|||||||
$invoice
|
$invoice
|
||||||
->setMerchantInfo(new MerchantInfo())
|
->setMerchantInfo(new MerchantInfo())
|
||||||
->setBillingInfo(array(new BillingInfo()))
|
->setBillingInfo(array(new BillingInfo()))
|
||||||
->setItems(array(new InvoiceItem()))
|
|
||||||
->setNote("Medical Invoice 16 Jul, 2013 PST")
|
->setNote("Medical Invoice 16 Jul, 2013 PST")
|
||||||
->setPaymentTerm(new PaymentTerm())
|
->setPaymentTerm(new PaymentTerm())
|
||||||
->setShippingInfo(new ShippingInfo());
|
->setShippingInfo(new ShippingInfo());
|
||||||
@@ -61,7 +60,8 @@ $billing[0]
|
|||||||
// ### Items List
|
// ### Items List
|
||||||
// You could provide the list of all items for
|
// You could provide the list of all items for
|
||||||
// detailed breakdown of invoice
|
// detailed breakdown of invoice
|
||||||
$items = $invoice->getItems();
|
$items = array();
|
||||||
|
$items[0] = new InvoiceItem();
|
||||||
$items[0]
|
$items[0]
|
||||||
->setName("Sutures")
|
->setName("Sutures")
|
||||||
->setQuantity(100)
|
->setQuantity(100)
|
||||||
@@ -70,6 +70,17 @@ $items[0]
|
|||||||
$items[0]->getUnitPrice()
|
$items[0]->getUnitPrice()
|
||||||
->setCurrency("USD")
|
->setCurrency("USD")
|
||||||
->setValue(5);
|
->setValue(5);
|
||||||
|
// Second Item
|
||||||
|
$items[1] = new InvoiceItem();
|
||||||
|
$items[1]
|
||||||
|
->setName("Injection")
|
||||||
|
->setQuantity(5)
|
||||||
|
->setUnitPrice(new Currency());
|
||||||
|
|
||||||
|
$items[1]->getUnitPrice()
|
||||||
|
->setCurrency("USD")
|
||||||
|
->setValue(5);
|
||||||
|
$invoice->setItems($items);
|
||||||
|
|
||||||
$invoice->getPaymentTerm()
|
$invoice->getPaymentTerm()
|
||||||
->setTermType("NET_45");
|
->setTermType("NET_45");
|
||||||
|
|||||||
@@ -48,9 +48,11 @@ class OAuthTokenCredentialTest extends \PHPUnit_Framework_TestCase
|
|||||||
'cache.enabled' => true,
|
'cache.enabled' => true,
|
||||||
'cache.FileName' => AuthorizationCacheTest::CACHE_FILE
|
'cache.FileName' => AuthorizationCacheTest::CACHE_FILE
|
||||||
);
|
);
|
||||||
|
$cred = new OAuthTokenCredential('clientId', 'clientSecret');
|
||||||
|
|
||||||
//{"clientId":{"clientId":"clientId","accessToken":"accessToken","tokenCreateTime":1421204091,"tokenExpiresIn":288000000}}
|
//{"clientId":{"clientId":"clientId","accessToken":"accessToken","tokenCreateTime":1421204091,"tokenExpiresIn":288000000}}
|
||||||
AuthorizationCache::push($config, 'clientId', 'accessToken', 1421204091, 288000000);
|
AuthorizationCache::push($config, 'clientId', $cred->encrypt('accessToken'), 1421204091, 288000000);
|
||||||
$cred = new OAuthTokenCredential('clientId', 'clientSecret');
|
|
||||||
$apiContext = new ApiContext($cred);
|
$apiContext = new ApiContext($cred);
|
||||||
$apiContext->setConfig($config);
|
$apiContext->setConfig($config);
|
||||||
$this->assertEquals('clientId', $cred->getClientId());
|
$this->assertEquals('clientId', $cred->getClientId());
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class AuthorizationCacheTest extends \PHPUnit_Framework_TestCase
|
|||||||
$tokens = json_decode($contents, true);
|
$tokens = json_decode($contents, true);
|
||||||
$this->assertNotNull($contents);
|
$this->assertNotNull($contents);
|
||||||
$this->assertEquals('clientId', $tokens['clientId']['clientId']);
|
$this->assertEquals('clientId', $tokens['clientId']['clientId']);
|
||||||
$this->assertEquals('accessToken', $tokens['clientId']['accessToken']);
|
$this->assertEquals('accessToken', $tokens['clientId']['accessTokenEncrypted']);
|
||||||
$this->assertEquals('tokenCreateTime', $tokens['clientId']['tokenCreateTime']);
|
$this->assertEquals('tokenCreateTime', $tokens['clientId']['tokenCreateTime']);
|
||||||
$this->assertEquals('tokenExpiresIn', $tokens['clientId']['tokenExpiresIn']);
|
$this->assertEquals('tokenExpiresIn', $tokens['clientId']['tokenExpiresIn']);
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ class AuthorizationCacheTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertNotNull($result);
|
$this->assertNotNull($result);
|
||||||
$this->assertTrue(is_array($result));
|
$this->assertTrue(is_array($result));
|
||||||
$this->assertEquals('clientId', $result['clientId']);
|
$this->assertEquals('clientId', $result['clientId']);
|
||||||
$this->assertEquals('accessToken', $result['accessToken']);
|
$this->assertEquals('accessToken', $result['accessTokenEncrypted']);
|
||||||
$this->assertEquals('tokenCreateTime', $result['tokenCreateTime']);
|
$this->assertEquals('tokenCreateTime', $result['tokenCreateTime']);
|
||||||
$this->assertEquals('tokenExpiresIn', $result['tokenExpiresIn']);
|
$this->assertEquals('tokenExpiresIn', $result['tokenExpiresIn']);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user