GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (114)

src/elevate/HVCommunicator.php (1 issue)

Severity
1
<?php
2
3
/**
4
 * @author ecompton
5
 */
6
7
namespace elevate;
8
use DOMDocument;
9
use Psr\Log\LoggerAwareInterface;
10
use Psr\Log\LoggerInterface;
11
use Psr\Log\NullLogger;
12
use SimpleXMLElement;
13
14
use elevate\Interfaces\HVCommunicatorInterface;
15
16
//Load the custom exceptions
17
use elevate\Exceptions\HVCommunicatorAuthenticationExpiredException;
18
use elevate\Exceptions\HVCommunicatorUserNotAuthenticatedException;
19
use elevate\Exceptions\HVCommunicatorGenericException;
20
use elevate\Exceptions\HVCommunicatorAccessDeniedException;
21
22
/**
23
 * Class HVCommunicator
24
 * Performs XML requests to HV and checks responses
25
 * @package elevate
26
 */
27
class HVCommunicator implements HVCommunicatorInterface, LoggerAwareInterface
28
{
29
30
    public static $version = 'HVCommunicator1.0';
31
32
    protected $healthVaultPlatform = 'https://platform.healthvault-ppe.com/platform/wildcat.ashx';
33
    protected $language = '';
34
    protected $country = '';
35
    protected $authenticatedWcRequest = null;
36
37
    // Passed in via the constructor
38
    private $appId;
39
    private $thumbPrint;
40
    private $privateKey;
41
    private $config; // array of additional parameters
42
43
    // Generated in the constructor
44
    private $sharedSecret;
45
    private $digest;
46
47
    // Saved responses from HealthVault
48
    protected $rawResponse;
49
    private $SXMLResponse;
50
    private $responseCode;
51
52
    //Useful for testing and debugging
53
    private $rawRequest;
54
    private $requestParameters;
0 ignored issues
show
The property $requestParameters is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
55
56
    // Call setLogger to change this from the default NullLogger
57
    private $logger = NULL;
58
59
    // This gets set in the connect method.
60
    private $authToken;
61
62
    /**
63
     *
64
     * @param $appId String The Healthvault Application ID
65
     * @param $thumbPrint String The Cert Thumbprint
66
     * @param $privateKey String The private key used to sign the request
67
     * @param $config Array Important session variables
68
     */
69
    public function __construct($appId, $thumbPrint, $privateKey, $config)
70
    {
71
        $this->appId = $appId;
72
        $this->thumbPrint = $thumbPrint;
73
        $this->privateKey = $privateKey;
74
        $this->config = $config;
75
        $this->logger = new NullLogger();
76
77
     if($this->digest == null){
78
        if (empty($this->config['digest'])) {
79
            $this->sharedSecret = $this->hashString(uniqid());
80
            $this->digest = $this->hmacSha1Content($this->sharedSecret, $this->sharedSecret);
81
        }
82
        else
83
        {
84
            $this->digest = $this->config['digest'];
85
        }
86
87
        if (!empty($this->config['authToken'])) {
88
            $this->authToken = $this->config['authToken'];
89
        }
90
     }
91
92
    }
93
94
    /**
95
     * Connects to HV and receives the auth token to use
96
     * @return mixed The auth token after a successful connection
97
     */
98
    public function connect()
99
    {
100
101
        // Use this document to help remove whitespace.
102
        $dom = new \DOMDocument('1.0');
103
        $dom->preserveWhiteSpace = false;
104
        $dom->formatOutput = false;
105
106
        // Grab an authToken from HV
107
        if (empty($this->authToken)) {
108
            $baseXML = file_get_contents(__DIR__ . '/XmlTemplates/CreateAuthenticatedSessionTokenTemplate.xml');
109
110
            $simpleXMLObj = simplexml_load_string($baseXML);
111
112
            // Set the attributes accordingly
113
            $simpleXMLObj->{'app-id'} = $this->appId;
114
            $appServer = $simpleXMLObj->credential->appserver;
115
            $appServer->content->{'shared-secret'}->{'hmac-alg'} = $this->digest;
116
            $appServer->content->{'app-id'} = $this->appId;
117
118
            // Get the has of the content
119
            $dom->loadXML($appServer->content->asXML());
120
            $contentXML = $dom->saveXML($dom->documentElement);
121
122
            $contentXMLSigned = $this->signRequest($contentXML);
123
124
            $appServer->sig['thumbprint'] = $this->thumbPrint;
125
            $appServer->sig = $contentXMLSigned;
126
127
            $dom->loadXML($simpleXMLObj->asXML());
128
            $newXML = $dom->saveXML($dom->documentElement);
129
130
            // throws HVCommunicatorAnonymousWcRequestException
131
            $this->anonymousWcRequest('CreateAuthenticatedSessionToken', '1', $newXML);
132
            //using DOMDocument, SXML has issues with HV's weird namespaceing
133
            $doc = new DOMDocument();
134
            $doc->loadXML($this->rawResponse);
135
136
137
            $this->authToken = $doc->getElementsByTagName('token')->item(0)->nodeValue;
138
        }
139
140
        return $this->authToken;
141
    }
142
143
    /**
144
     * @param $method String The HV XML method to use
145
     * @param $methodVersion String The version of the HV XML method to use
146
     * @param $info String The information to send
147
     * @param $additionalHeaders array Additional headers to add to the request
148
     * @param $personId String The HV person id to send with the request
149
     */
150
    public function makeRequest($method, $methodVersion, $info,  $additionalHeaders, $personId)
151
    {
152
        $xml = null;
153
        $online = $this->isOnlineRequest($personId);
154
        if($online)
155
        {
156
            $xml = file_get_contents(__DIR__ . '/XmlTemplates/AuthenticatedWcRequestTemplate.xml');
157
            $simpleXMLObj = simplexml_load_string($xml);
158
            $simpleXMLObj->{'header'}->{'auth-session'}->{'user-auth-token'} = $this->config['wctoken'];
159
            $xml = $simpleXMLObj->asXML();
160
        }
161
        else
162
        {
163
            $xml = file_get_contents(__DIR__ . '/XmlTemplates/OfflineRequestTemplate.xml');
164
            $simpleXMLObj = simplexml_load_string($xml);
165
            $simpleXMLObj->{'header'}->{'auth-session'}->{'offline-person-info'}->{'offline-person-id'} = $personId;
166
            $xml = $simpleXMLObj->asXML();
167
        }
168
        //Do this first, record-id generally needs to be tacked on
169
        $xml = $this->setupAdditionalHeaders($xml,$additionalHeaders);
170
171
        $xml = $this->setupRequestInfo($xml, $method, $methodVersion, $info);
172
173
        $this->makeHVRequest($xml);
174
175
    }
176
177
    /**
178
     * Convenience function for replacing the various xml tags with relevant information
179
     * @param $xml String The XML to be changed
180
     * @param $method String The HV method to use
181
     * @param $methodVersion String The version of the HV method
182
     * @param $info String Info to be added (if applicable)
183
     * @return mixed The newly filled in XML
184
     */
185
    private function setupRequestInfo($xml, $method, $methodVersion, $info)
186
    {
187
        $infoReplacement = null;
188
        $dom = new \DOMDocument('1.0');
189
        $dom->preserveWhiteSpace = false;
190
        $dom->formatOutput = false;
191
        if(!empty($info))
192
        {
193
            $infoReplacement =  $info;
194
195
 		    if(strpos($infoReplacement,'<info>') === false)
196
            {
197
                $infoReplacement =  '<info>'.$info.'</info>';
198
199
 		    }
200
        }
201
        else
202
        {
203
            $infoReplacement = '<info />';
204
        }
205
        $simpleXMLObj = simplexml_load_string($xml);
206
207
        // Set the attributes accordingly
208
        $simpleXMLObj->{'header'}->{'method'} = $method;
209
        $simpleXMLObj->{'header'}->{'method-version'} = $methodVersion;
210
        $simpleXMLObj->{'header'}->{'msg-time'} = gmdate("Y-m-d\TH:i:s");
211
        $simpleXMLObj->{'header'}->{'version'} = self::$version;
212
        $simpleXMLObj->{'header'}->{'language'} = 'en';
213
        $simpleXMLObj->{'header'}->{'country'} = 'US';
214
        $infoXMLObj = simplexml_load_string($infoReplacement);
215
        //Theses not present in AuthToken Request
216
        if($method !='CreateAuthenticatedSessionToken')
217
        {
218
            $simpleXMLObj->{'header'}->{'auth-session'}->{'auth-token'} = $this->authToken;
219
            $dom->loadXML($infoXMLObj->asXML());
220
            $infoHashXML = $dom->saveXML($dom->documentElement);
221
            $simpleXMLObj->{'header'}->{'info-hash'}->{'hash-data'} = $this->hashString($infoHashXML);
222
        }
223
        // Create new DOMElements from the two SimpleXMLElements
224
        $domRequest = dom_import_simplexml($simpleXMLObj);
225
        $domInfo  = dom_import_simplexml($infoXMLObj);
226
227
        // Import the <info> into the request
228
        $domInfo  = $domRequest->ownerDocument->importNode($domInfo, TRUE);
229
230
        // Append the <info> to request
231
        $domRequest->appendChild($domInfo);
232
233
        // Get the header XML
234
        $headerS = $simpleXMLObj->{'header'};
235
       $dom->loadXML($headerS->asXML());
236
       $headerXML = $dom->saveXML($dom->documentElement);
237
238
       $headerXMLHashed = $this->hmacSha1Content($headerXML, base64_decode($this->digest));
239
        //Not present in Auth token request
240
        if($method !='CreateAuthenticatedSessionToken')
241
        {
242
            $simpleXMLObj->{'auth'}->{'hmac-data'} = $headerXMLHashed;
243
        }
244
        $dom->loadXML($simpleXMLObj->asXML());
245
        $newXML = $dom->saveXML($dom->documentElement);
246
247
        return $newXML;
248
249
    }
250
251
    /**
252
     * Adds additional tags to the XML request
253
     * @param $xml String The XML to change
254
     * @param $additionalHeaders  array New tags to be added to the template if needed
255
     * @return mixed The newly altered XML
256
     */
257
    private function setupAdditionalHeaders($xml,$additionalHeaders)
258
    {
259
        $sxe = new SimpleXMLElement($xml);
260
        if (!empty($additionalHeaders)) {
261
            foreach ($additionalHeaders as $element => $text) {
262
                if($text != null){
263
                    $newHeader = "<" . $element . ">" . $text . "</" . $element . ">";
264
265
                    // New element to be inserted
266
                    $insert = new SimpleXMLElement($newHeader);
267
                    // Get the method-version element
268
                    $target = current($sxe->xpath('//method-version'));
269
                    // Insert the new element after the method-version element
270
                    $this->simplexml_insert_after($insert, $target);
271
                }
272
            }
273
        }
274
        return $sxe->asXML();
275
    }
276
277
    private function simplexml_insert_after(SimpleXMLElement $insert, SimpleXMLElement $target)
278
    {
279
        $target_dom = dom_import_simplexml($target);
280
        $insert_dom = $target_dom->ownerDocument->importNode(dom_import_simplexml($insert), true);
281
        if ($target_dom->nextSibling) {
282
            return $target_dom->parentNode->insertBefore($insert_dom, $target_dom->nextSibling);
283
        } else {
284
            return $target_dom->parentNode->appendChild($insert_dom);
285
        }
286
    }
287
288
    /**
289
     * Checks to see if the the person is online, if not checks to make sure they are at least authenticated
290
     * @param $personId
291
     * @throws HVCommunicatorUserNotAuthenticatedException
292
     * @return bool
293
     */
294
    private function isOnlineRequest($personId)
295
    {
296
        //check to see if we have a token
297
        if (empty($this->config['wctoken'])) {
298
            // No token, is the user authenticated?
299
            if (empty($personId)) {
300
                throw new HVCommunicatorUserNotAuthenticatedException();
301
            }
302
            return false;
303
        } else {
304
            return true;
305
        }
306
    }
307
308
309
    /**
310
     * Setups the XML to make the WC Request using the anonymous template
311
     * @param $method HV method to use
312
     * @param string $methodVersion The version of the HV method to use
313
     * @param string $info The info to put in the request
314
     * @param array $additionalHeaders Any additional tags to add
315
     */
316
    public function anonymousWcRequest($method, $methodVersion = '1', $info = '', $additionalHeaders = array())
317
    {
318
        // Use this document to help remove whitespace.
319
        $dom = new \DOMDocument('1.0');
320
        $dom->preserveWhiteSpace = false;
321
        $dom->formatOutput = false;
322
        $baseXML = file_get_contents(__DIR__ . '/XmlTemplates/AnonymousWcRequestTemplate.xml');
323
324
        $simpleXMLObj = simplexml_load_string($baseXML);
325
326
        // Set the attributes accordingly
327
        $simpleXMLObj->{'header'}->{'app-id'} = $this->appId;
328
329
330
        $dom->loadXML($simpleXMLObj->asXML());
331
        $newXML = $dom->saveXML($dom->documentElement);
332
        $newXML = $this->setupAdditionalHeaders($newXML, $additionalHeaders);
333
        $newXML = $this->setupRequestInfo($newXML, $method, $methodVersion, $info);
334
335
        $newXML = $this->setupAdditionalHeaders($newXML, $additionalHeaders);
336
337
        $this->makeHVRequest($newXML);
338
    }
339
340
    /**
341
     * Makes the actual request to HV and checks the response to see if request was succesful
342
     * @param $xml String The XML to send
343
     */
344
    private function makeHVRequest($xml)
345
    {
346
347
        $dom = new \DOMDocument('1.0');
348
        $dom->preserveWhiteSpace = false;
349
        $dom->formatOutput = false;
350
        $dom->loadXML($xml);
351
        $formattedDOMXML = $dom->saveXML();
352
353
354
        $params = array(
355
            'http' => array(
356
                'method' => 'POST',
357
                // remove line breaks and spaces between elements, otherwise the signature check will fail
358
                'content' => $formattedDOMXML,
359
            ),
360
        );
361
362
        $this->logger->debug('New Request: ' . $params['http']['content']);
363
        $this->rawRequest = $params['http']['content'];
364
365
        $this->rawResponse = $this->performRequest();
366
        $this->processResponse();
367
    }
368
369
    /**
370
     * Makes a cURL request and returns the response
371
     * @return string The Raw XML response
372
     */
373
    private function performRequest()
374
    {
375
        $c = curl_init($this->healthVaultPlatform);
376
        curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
377
        curl_setopt($c, CURLOPT_POST, true);
378
        curl_setopt($c, CURLOPT_POSTFIELDS, $this->rawRequest);
379
        curl_setopt($c, CURLOPT_VERBOSE, 1);
380
        curl_setopt($c, CURLOPT_HEADER, 1);
381
        curl_setopt($c, CURLINFO_HEADER_OUT, 1);
382
383
        $response = curl_exec($c);
384
385
386
        $header_size = curl_getinfo($c,CURLINFO_HEADER_SIZE);
387
        $result['header'] = substr($response, 0, $header_size);
388
        $result['body'] = substr( $response, $header_size );
389
        $result['http_code'] = curl_getinfo($c,CURLINFO_HTTP_CODE);
390
391
392
        $info = curl_getinfo($c);
393
        $headerSent = curl_getinfo($c, CURLINFO_HEADER_OUT);
394
395
        curl_close($c);
396
        return $result['body'];
397
    }
398
399
    /**
400
     * Processes the XML response and checks for errors
401
     * @throws HVCommunicatorAuthenticationExpiredException
402
     * @throws \Exception
403
     */
404
    private function processResponse()
405
    {
406
        if (!$this->rawResponse) {
407
            $this->responseCode = -1;
408
            throw new \Exception('HealthVault Connection Failure', -1);
409
        }
410
        $this->logger->debug('New Response: ' . $this->rawResponse);
411
412
        $this->SXMLResponse = simplexml_load_string($this->rawResponse);
413
414
        $this->responseCode = (int)$this->SXMLResponse->status->code;
415
416
        if ($this->responseCode > 0) {
417
            switch ($this->responseCode)
418
            {
419
                case 11: //Access Denied (Bad Auth Token)
420
                    throw new HVCommunicatorAccessDeniedException($this->SXMLResponse->status->error->message, $this->responseCode);
421
                case 7: // The user authenticated session token has expired.
422
                case 65: // The authenticated session token has expired.
423
                    unset($this->config);
424
                    unset($this->authToken);
425
                    throw new HVCommunicatorAuthenticationExpiredException($this->SXMLResponse->status->error->message, $this->responseCode);
426
                default: // Handle all status's without a particular case
427
                    throw new HVCommunicatorGenericException($this->SXMLResponse->status->error->message,$this->responseCode);
428
            }
429
        }
430
    }
431
432
    /**
433
     * Setup the redirect URL for sending the user to authenticate the App with HealthVault
434
     *
435
     * @param string $appId                     - The HV App ID
436
     * @param string $redirect                  - the URL to redirect to
437
     * @param array $config                     - Important session variables
438
     * @param array  $additionalTargetQSParams  - Any additional parameters to append to the URL
439
     * @param string $healthVaultAuthInstance   - The current HV instance to use
440
     * @param string $target                    - The goal of the request
441
     *
442
     * @return string The URL to go to for authorization, including the urlencoded redirect URL
443
     */
444
    public static function getAuthenticationURL($appId, $redirect, $config,
445
                                                $additionalTargetQSParams = NULL,
446
                                                $healthVaultAuthInstance = 'https://account.healthvault-ppe.com/redirect.aspx',
447
                                                $target = 'AUTH')
448
    {
449
450
        if (empty($config['healthVault']['redirectToken']))
451
        {
452
            $config['healthVault']['redirectToken'] = md5(uniqid());
453
        }
454
455
        $redirectUrl = urlencode('?appid='.$appId.'&redirect='.$redirect.'&redirectToken='.$config['healthVault']['redirectToken']);
456
457
        // If the additional parameters are not in associative array then do nothing.
458
        if (is_array($additionalTargetQSParams))
459
        {
460
            foreach ($additionalTargetQSParams as $paramKey => $paramData)
461
            {
462
                $redirectUrl .=  urlencode('&' . $paramKey . '=' . $paramData);
463
            }
464
        }
465
466
        $url = $healthVaultAuthInstance.'?target='.$target.'&targetqs='.$redirectUrl;
467
        return $url;
468
    }
469
470
    /**
471
     * Invalidates the current session
472
     * @param $config array Session values
473
     */
474
    public static function invalidateSession(&$config)
475
    {
476
        unset($config);
477
    }
478
479
    public function clearAuthToken()
480
    {
481
        $this->authToken = NULL;
482
    }
483
484
    /**
485
     * Returns the raw xml response
486
     * @return mixed
487
     */
488
    public function getRawResponse(){
489
        return $this->rawResponse;
490
    }
491
492
    /**
493
     * Returns the SimpleXML representation of the response
494
     * @return mixed
495
     */
496
    public function getSXMLResponse(){
497
        return $this->SXMLResponse;
498
    }
499
500
    /**
501
     * Sets the logger to use
502
     * @param LoggerInterface $logger
503
     * @return null|void
504
     */
505
    public function setLogger(LoggerInterface $logger)
506
    {
507
        $this->logger = $logger;
508
    }
509
510
    /**
511
     * Sets the HV platform to use
512
     * @param $healthVaultPlatform
513
     */
514
    public function setHealthVaultPlatform($healthVaultPlatform)
515
    {
516
        $this->healthVaultPlatform = $healthVaultPlatform;
517
    }
518
519
520
    /**
521
     * @return mixed
522
     */
523
    public function getDigest()
524
    {
525
        return $this->digest;
526
    }
527
528
    /** Hash
529
     * @param $str
530
     * @return string
531
     */
532
    private function hashString($str)
533
    {
534
535
        $hash = trim(base64_encode(sha1($str, TRUE)));
536
        return $hash;
537
    }
538
539
    /** Hmac Sha 1
540
     * @param $str
541
     * @param $key
542
     * @return string
543
     */
544
    private function hmacSha1Content($str, $key)
545
    {
546
547
        $hmac =  trim(base64_encode(hash_hmac('sha1', $str, $key, TRUE)));
548
        return $hmac;
549
    }
550
551
    /**
552
     * Signs the appropriate XML information for HV to validate
553
     * @param $str The XML to sign
554
     * @return string The signed XML
555
     * @throws \Exception
556
     */
557
    protected function signRequest($str)
558
    {
559
        static $privateKey = NULL;
560
561
        if (is_null($privateKey)) {
562
            if (is_file($this->privateKey)) {
563
                if (is_readable($this->privateKey)) {
564
                    $privateKey = @file_get_contents($this->privateKey);
565
                } else {
566
                    throw new \Exception('Unable to read private key file.');
567
                }
568
            } else {
569
                $privateKey = $this->privateKey;
570
            }
571
        }
572
573
        // TODO check if $privateKey really is a key (format)
574
575
        openssl_sign(
576
        // remove line breaks and spaces between elements, otherwise the signature check will fail
577
            $str,
578
            $signature,
579
            $privateKey,
580
            OPENSSL_ALGO_SHA1);
581
        $sig = trim(base64_encode($signature));
582
        return $sig;
583
    }
584
585
    /**
586
     * Gets the raw XML request sent
587
     * @return mixed
588
     */
589
    public function getRawRequest()
590
    {
591
        return $this->rawRequest;
592
    }
593
594
595
}
596
597