diff options
-rw-r--r-- | README.md | 73 | ||||
-rwxr-xr-x | gsxcl | 9 | ||||
-rw-r--r-- | gsxlib.php | 128 | ||||
-rw-r--r-- | runtests.php | 22 |
4 files changed, 160 insertions, 72 deletions
@@ -10,27 +10,30 @@ validation (as opposed to burdening Apple's servers with totally invalid request Requrements =========== -- SOAP support in your PHP installation -- A GSX account with the "Can access Web Services" privilege enabled +- SOAP support in your PHP +- Client certificates for GSX access +- Whitelisted IP address of the source of your requests +- GSX account with the "Can access Web Services" privilege enabled + Usage ===== Best illustrated with a simple example: -```php -<?php - - include 'gsxlib/gsxlib.php'; - $gsx = GsxLib::getInstance($sold_to, $username, $password); - $info = $gsx->warrantyStatus($serialnumber); - echo $info->productDescription; - > MacBook Pro (15-inch 2.4/2.2GHz) + <?php -?> -``` + include 'gsxlib/gsxlib.php'; + $_ENV['GSX_CERT'] = '/path/to/gsx/client/cert.pem'; + $_ENV['GSX_KEYPASS'] = 'MySuperSecretPrivateKeyPassPhrase'; + $gsx = GsxLib::getInstance($sold_to, $username); + $info = $gsx->warrantyStatus($serialnumber); + echo $info->productDescription; + > MacBook Pro (15-inch 2.4/2.2GHz) + + ?> -US users should remember to set the fifth argument of the constructor to 'am'. +If you're in the US, remember to set the fifth argument to the constructor to 'am'. gsxcl @@ -39,8 +42,48 @@ gsxcl The package includes a rudimentary command line client to the GSX API called _gsxcl_. It can perform various functions in the library and is meant mainly as a simple test tool for the library. -##License## - + +FAQ +=== + +### Q: How do I create the necessary PEM file? +A: The PEM file must be a concatenation of the certificate you got from Apple and your private key file. You can create this from the Terminal: + + $ cat Applecare-APP1234-0000123456.Test.apple.com.chain.pem privatekey.pem > certbundle.pem + +After that you would use _certbundle.pem_ as your client certificate. The contents of _certbundle.pem_ should look something like this: + + -----BEGIN CERTIFICATE----- + BLASOQ()*Q#()**)REW)*(EW*)*E)WUR)*EW(UR) + ... + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + 0990320003q43090435J403439590S-S=DS=- + ... + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + )_#_)#)$IK_#@))KDE_)FD_SF)DSF_DS)FDS_FDSFSD + .... + -----END CERTIFICATE----- + -----BEGIN RSA PRIVATE KEY----- + Proc-Type: .... + DEK-Info: ... + BUNCH OF GIBBERISH + -----END RSA PRIVATE KEY----- + + +### Q: Do I need to make changes to my web server configuration for the SSL authentication to work? +A: No, the library takes care of everything. That's why the certificate path and passphrase are implemented as environment variables. This +ensures the certificate is sent with each request and you only have to define the paths once in your code. + +### Q: How can I remove the passphrase from my private key? + + $ openssl rsa -in privatekey.pem -out privatekey.nopass.pem + + +License +======= + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Version 2, December 2004 Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> @@ -25,11 +25,10 @@ require 'gsxlib.php'; if( count( $argv ) < 6 ) { echo <<<EOT -usage: gsxcl [--debug] -s sold-to -u username -p password [-r region] [-e environment] [-f format] verb noun +usage: gsxcl [--debug] -s sold-to -u username [-r region] [-e environment] [-f format] verb noun --debug Report all errors -s sold-to your GSX Sold-To account -u username the Apple ID with GSX WS API access - -p password the password for the Apple ID -r region either "am" (America), "emea" (default, Europe, Middle-East, Africa) "apac" (Asia-Pacific) or "la" (Latin America) -e environment the GSX environment. Either empty (production, default), "it" or "ut" @@ -74,7 +73,11 @@ switch( $noun ) } } -$gsx = GsxLib::getInstance( $opts['s'], $opts['u'], $opts['p'], $environment, $region ); +if (!$_ENV['GSX_CERT']) { + exit("The GSX_CERT environment variable must be set to the path to your GSX client certificate"); +} + +$gsx = GsxLib::getInstance( $opts['s'], $opts['u'], $environment, $region ); if( isset( $opts['d'] )) { $data = $opts['d']; @@ -13,37 +13,31 @@ */ class GsxLib { - private $client; - private $region; - private $session_id; - private $environment; + private $client; // SoapClient instance + private $region; // GSX region code + private $session_id; // Current GSX SessionID + private $environment; // Current GSX environment - //IT: https://gsxws%s.apple.com/wsdl/%sAsp/gsx-%sAsp.wsdl - //PROD: https://gsxws2.apple.com/wsdl/%sAsp/gsx-%sAsp.wsdl - private $wsdl = 'https://gsxws%s.apple.com/wsdl/%sAsp/gsx-%sAsp.wsdl'; + private $wsdl = 'https://gsxapi%s.apple.com/wsdl/%sAsp/gsx-%sAsp.wsdl'; - static $_instance; + static $_instance; // Reference to GsxLib instance const timeout = 30; // session timeout in minutes public static function getInstance( - $account, + $sold_to, $username, - $password, $environment = '', $region = 'emea', - $tz = 'CEST', - $lang = 'en') + $tz = 'CEST') { if(!(self::$_instance instanceof self)) { self::$_instance = new self( - $account, + $sold_to, $username, - $password, $environment, $region, - $tz, - $lang + $tz ); } @@ -54,20 +48,18 @@ class GsxLib private function __clone() {} private function __construct( - $account, + $sold_to, $username, - $password, $environment = '', $region = 'emea', - $tz = 'CEST', - $lang = 'en' ) + $tz = 'CEST' ) { if(!class_exists('SoapClient')) { throw new GsxException('Looks like your PHP lacks SOAP support'); } - if(!preg_match('/\d+/', $account)) { - throw new GsxException('Invalid Sold-To: ' . $account); + if(!preg_match('/\d+/', $sold_to)) { + throw new GsxException('Invalid Sold-To: ' . $sold_to); } $regions = array('am', 'emea', 'apac', 'la'); @@ -89,31 +81,41 @@ class GsxLib $error = sprintf($error, $environment, implode(', ', $envirs)); throw new GsxException($error); } - } else { - // GSX2... - $environment = '2'; } + $this->cert_path = $_ENV['GSX_CERT']; + $this->cert_pass = $_ENV['GSX_KEYPASS']; + + if (!is_readable($this->cert_path)) { + throw new GsxException("Cannot read SSL certificate"); + } + $this->wsdl = sprintf($this->wsdl, $environment, $region, $region); + $client_options = array( + 'trace' => TRUE, + 'exceptions' => TRUE, + 'local_cert' => $this->cert_path, + 'passphrase' => $this->cert_pass, + ); + $this->client = new SoapClient( - $this->wsdl, array('exceptions' => TRUE, 'trace' => 1) + $this->wsdl, $client_options ); if(!$this->client) { throw new GsxException('Failed to create SOAP client.'); } - if(@$_SESSION['_gsxlib_timeout'][$account] > time()) { - // return $this->session_id = $_SESSION['_gsxlib_id'][$account]; + if(@$_SESSION['_gsxlib_timeout'][$sold_to] > time()) { + // return $this->session_id = $_SESSION['_gsxlib_id'][$sold_to]; } $a = array( 'AuthenticateRequest' => array( 'userId' => $username, - 'password' => $password, - 'serviceAccountNo' => $account, - 'languageCode' => $lang, + 'serviceAccountNo' => $sold_to, + 'languageCode' => 'en', 'userTimeZone' => $tz, ) ); @@ -124,19 +126,24 @@ class GsxLib ->AuthenticateResponse ->userSessionId; } catch(SoapFault $e) { - if($environment == '2') $environment = 'production'; + syslog(LOG_ERR, $e); + syslog(LOG_ERR, $this->wsdl); + syslog(LOG_ERR, $this->client->__getLastRequest()); + syslog(LOG_ERR, $this->client->__getLastResponse()); + syslog(LOG_ERR, $this->client->__getLastResponseHeaders()); + + if($environment == '') $environment = 'production'; - $error = 'Authentication with GSX failed. Does this account have access to ' - .$environment." environment?\n"; + $error = 'Authentication with GSX failed. Does this account have access to '.$environment."?\n"; throw new GsxException($error); } // there's a session going, put the credentials in there if(session_id()) { - $_SESSION['_gsxlib_id'][$account] = $this->session_id; + $_SESSION['_gsxlib_id'][$sold_to] = $this->session_id; $timeout = time()+(60*self::timeout); - $_SESSION['_gsxlib_timeout'][$account] = $timeout; + $_SESSION['_gsxlib_timeout'][$sold_to] = $timeout; } } @@ -181,6 +188,24 @@ class GsxLib } + /** + * The Reported Symptom/Issue API allows partners to fetch the information related to symptoms and issues. + * If all the validations go through, api returns a list of valid symptoms/issues according to the input data. + * Otherwise api returns appropriate error message. + * @param mixed $query + */ + public function fetchSymptomIssue($query) + { + if(!is_array($query)) { + $like = self::looksLike($query); + $query = array($like => $query); + } + + $rd = array('ReportedSymptomIssue' => array('requestData' => $query)); + return $this->request($rd)->reportedSymptomIssueResponse; + + } + public function fetchiOsActivation($query) { if(!is_array($query)) { @@ -277,23 +302,28 @@ class GsxLib 'unreceivedModules' => 'N', ); - // provide "shortcuts" for dispatch ID and SN - switch(self::looksLike($query)) { - case 'dispatchId': - $query = array('repairConfirmationNumber' => $query); - break; - case 'serialNumber': - $query = array('serialNumber' => $query); - break; - } + if(!is_array($query)) { + // provide "shortcuts" for dispatch ID and SN + switch(self::looksLike($query)) { + case 'dispatchId': + $query = array('repairConfirmationNumber' => $query); + break; + case 'serialNumber': + $query = array('serialNumber' => $query); + break; + } - $query = array_merge($fields, $query); + $query = array_merge($fields, $query); + + } + $req = array( 'RepairLookupRequest' => array( 'userSession' => array('userSessionId' => $this->session_id), 'lookupRequestData' => $query )); - $response = $this->client->RepairLookup($req)->RepairLookupResponse; + $response = $this->client->RepairLookup($req) + ->RepairLookupResponse; return $response->lookupResponseData; } @@ -438,7 +468,9 @@ class GsxLib } - $req = array('PartsLookup' => array('lookupRequestData' => $query)); + $req = array('PartsLookup' => array( + 'lookupRequestData' => $query + )); $result = $this->request($req)->parts; // always return an array diff --git a/runtests.php b/runtests.php index 6619cc5..fa87c70 100644 --- a/runtests.php +++ b/runtests.php @@ -6,23 +6,31 @@ require_once('gsxlib.php'); class GsxlibTest extends UnitTestCase { function setUp() { - global $argv; - $this->gsx = GsxLib::getInstance($argv[1], $argv[2], $argv[3], 'ut', 'emea', 'CEST', 'es'); + $this->sn = $_ENV['GSX_SN']; + $this->gsx = GsxLib::getInstance($_ENV['GSX_SOLDTO'], $_ENV['GSX_USER'], 'ut'); } function testWarranty() { - $wty = $this->gsx->warrantyStatus('RM6501PXU9C'); + $wty = $this->gsx->warrantyStatus($this->sn); $this->assertEqual($wty->warrantyStatus, 'Out Of Warranty (No Coverage)'); } + function testSymptomIssue() { + $r = $this->gsx->fetchSymptomIssue($this->sn); + $this->assertEqual($r->symptoms[0]->reportedSymptomCode, 6115); + $this->assertEqual($r->symptoms[1]->reportedSymptomDesc, "Accidental damage"); + } + function testCreateCarryInRepair() { + $symptom = $this->gsx->fetchSymptomIssue($this->sn)->symptoms[0]; + $repairData = array( 'shipTo' => '6191', - 'serialNumber' => 'RM6501PXU9C', + 'serialNumber' => $this->sn, 'diagnosedByTechId' => 'USA022SN', 'symptom' => 'Sample symptom', 'diagnosis' => 'Sample diagnosis', - 'unitReceivedDate' => '07/02/13', + 'unitReceivedDate' => '07/25/15', 'unitReceivedTime' => '12:40 PM', 'notes' => 'A sample notes', 'poNumber' => '11223344', @@ -47,13 +55,15 @@ class GsxlibTest extends UnitTestCase 'lastName' => 'Customer lastname', 'primaryPhone' => '4088887766' ), + 'reportedSymptomCode' => $symptom->reportedSymptomCode, + 'reportedIssueCode' => 'IP025', ); $this->gsx->createCarryinRepair($repairData); } - function testCreateMailInRepair() { + function _testCreateMailInRepair() { $repairData = array( 'shipTo' => '6191', 'accidentalDamage' => false, |