gRPC PHP client HTTP headers - php

I am attempting to set some HTTP headers in a gRPC client call written in PHP. I have read all the documentation I can find for the PHP implementation of gRPC but cannot find anything specifying how to do this in PHP. From reading docs for other languages I have come to think that headers are specified in the client metadata. However, I can't find anything on how these should be formatted in php, and all the formats I try don't seem to work. Here is my current code:
$options = [
'credentials' => $this->credentialsObject,
'update_metadata' => function($metaData){
$metaData['headers'] = ['Authorization' => 'Bearer ' . $this->token];
return $metaData;
}
];
$client = new OrganizationServiceClient($this->url,$options);
$r = new \Google\Protobuf\GPBEmpty();
list($data,$status) = $client->list($r)->wait();
The response I get from that is the same as if I don't set the authorization header at all (Access Denied!), though I have been told that my user should have permission to view that resource.
I don't have access to any server logs to help with debugging on that side (though I am trying to get access to them - might be able to in the next day).
Any help or pointers would be appreciated. I'm been working on this for a few days now and feel like I've tried everything I can think of.
Thanks!

I was able to get the info I needed to solve the problem by asking a question in the grpc.io google group. Here is that thread: https://groups.google.com/forum/#!searchin/grpc-io/php%7Csort:date/grpc-io/p4-P78_EOyY/pHHR6Q5OBwAJ.
The gist of the solution is that gRPC uses HTTP2 (so different header syntax), with metadata being equivalent to headers. Below is my updated code. Here is the important line $metaData['authorization'] = ['Bearer ' . $this->token];. Notice that the $metaData array key is the same as the HTTP2 header key, and the value is an array containing the header value as a string.
$options = [
'credentials' => $this->credentialsObject,
'update_metadata' => function($metaData){
$metaData['authorization'] = ['Bearer ' . $this->token];
return $metaData;
}
];
$client = new OrganizationServiceClient($this->url,$options);
$r = new \Google\Protobuf\GPBEmpty();
list($data,$status) = $client->list($r)->wait();

Related

What is the benefit of using REST API instead of my simplistic approach?

I read an article on how to create a REST API.
While my APIs (in /app/api/ folder) normally just check $_POST parameters and echo json_encode($response); die; after doing some database manipulations, I find here in the article, that some headers are set, which I don't normally do.
Why is that necessary and/or is it better to do it that way?
Will I still be able to get the JSON result from JavaScript using Fetch API if I convert my code to REST API?
I saw there are SOAP clients as well (and I have to do some reading on that as well), but I'm curious which of these three (or possibly any other) ways is usually the best.
It seems to me, that my way is easier for fetching with JavaScript, but perhaps it's also good enough make API calls (using CURL?) from PHP directly.
My usual example:
require_once __DIR__ . '/../../init.php';
require_once env('SHOP_ROOT') . '/inc_functions.php';
$cmd = $_REQUEST['cmd'] ?? null;
$token = $_REQUEST['token'] ?? null;
if ($token !== env('API_TOKEN'))
json_response(false, ['Incorrect token']);
/*--------------------------------------------------------*
* cmd : delete *
*--------------------------------------------------------*
* parameters : user, uploadId *
*--------------------------------------------------------*/
if ($cmd == 'delete') {
$email = $_REQUEST['user'] ?? '';
$uploadId = intval($_REQUEST['uploadId'] ?? 0);
$selClientQ = <<<SQL
SELECT id_client
FROM client
WHERE
email = ? AND
is_active = 1 AND
is_banned = 0
SQL;
$clientId = data_select($selClientQ, $email)[0]['id_client'] ?? 0;
$delClientUploadQ = <<<SQL
DELETE FROM client_uploads WHERE client_id = ? AND id = ?
SQL;
$isDeleted = data_delete($delClientUploadQ, $clientId, $uploadId);
json_response($isDeleted, [
'clientId' => $clientId,
'success' => $isDeleted,
]);
}
data_select, data_delete and json_response are of course my own functions, where the first two allow me to avoid all those lines for mysqli prepared statements and binding parameters, and the latter is basically the same json_encode only with some headers before (giving 200 or 500 HTTP response based on the boolean) and exiting script execute with die afterwards.
"Why is that necessary and/or is it better to do it that way?"
It looks like you're referring to the Cross-Origin Resource Sharing (CORS) headers. These headers are used to increase the security of your REST API and allow you to control which websites can actually call your API. Basically, if you set your 'Access-Control-Allow-Origin' header to your website's address, only your website can call this API. You can also have a look at this link which describes how this works: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
"Will I still be able to get the JSON result from JavaScript using Fetch API if I convert my code to REST API?"
Definitely :) As long as you ensure that you're setting the 'Accept' HTTP header to 'application/json' on your request and your API responds with a 'Content-Type' header of 'application/json'.
Here is a link showing how that works: https://javascript.info/fetch
If you want to venture into the world of SOAP, I'd recommend you rather look into GRPC. SOAP is mostly used in legacy systems nowadays

Not possible to download file using PHP's Microsoft Graph SDK

I have a service app and I'm willing to download a file from an user's Drive using msgraph-sdk
I am able to upload a file using
$graph->createRequest("PUT", "/users/".$userId."/drive/items/root:/".$folderName."/".$fileName.":/content")
->upload($filePath);
But I am not able to download it. Here is the code I am using:
$graph = new Graph();
$graph->setAccessToken($accessToken);
$graph->createRequest("GET", "/users/".$userId."/drive/items/".$docId."/content")
->download($filePath);
The error is: PHP Warning: fclose(): 16 is not a valid stream resource in microsoft-graph/src/Http/GraphRequest.php on line 344
I could note that making a request to the URL https://graph.microsoft.com/v1.0/users/{USER_ID}/drive/items/{DOC_ID}/content using Postman and passing Authorization Bearer {ACCESS_TOKEN}, the response is the file's content, which is the expected behavior, but for some reason, calling it via PHP is not working.
Am I doing something wrong?
---- UPDATE ----
The problem seems to be related to the response I am getting after making the request. Documentation says that the response code will be 302 and I need to redirect to the address in Location header, but it looks like that the MS Graph (or Guzzle Client) is not being capable to redirect to the address. I tried editing GraphRequest.php to enable it when creating a Client like this:
$clientSettings = [
'base_uri' => $this->baseUrl,
'verify' => false,
'allow_redirects' => true,
'headers' => $this->headers
];
$client = new Client($clientSettings);
but it didn't work.
I believe this is a bug. I have logged it here. There is a fix checked in and we are getting ready to publish a new version of the SDK, so you should be able to pull in the fix soon. In the meantime, you can incorporate the PR and verify that it solves the problem that you are seeing.

PHP Soap - Authentication header missing

I have been using an API for a WMS which has updated to include authentication headers. I have been provided some required details but have been unable to sucessfully use the API. I have asked the developers but they are unable to help as they do not use PHP.
Previous to the last update, this would work:
$wsdl = URL_HERE;
$soapClient = new SoapClient($wsdl);
$params = array('customer' => $get_users_company->custcode_code);
$response = $soapClient->GetProducts($params);
With the authentification headers, this is what I currently have which is causing the error Authentication header missing
$wsdl = URL_HERE;
$ns = NAMESPACE_HERE;
$soapClient = new SoapClient($wsdl);
$headerbody = array('ID' => 'PROVIDED_ID_HERE', 'KEY' => 'PROVIDED_KEY_HERE');
$headers = new SOAPHeader($ns, 'AuthHeader', $headerbody);
$soapClient->__setSoapHeaders($headers);
$response = $soapClient->__soapCall("GetProducts", array('customer' => $get_users_company->custcode_code));
I'm not 100% sure I am doing this correctly, but without the last line, I get no errors and the page loads fine (No results). Am I correct in thinking the headers are being sent?
I have heard the good old, "we can't help because we are XML Gods and your little php is beneath us"...but you can still get technical support from them by speaking their XML language. Dump out your actual, raw XML and communicate with them using that - don't mention PHP.
Follow the example here and get your request. Make sure it is matching what the documentation of your API is requesting. If it is, call your technical support and show them your XML. If it isn't, then, you know what you need to fix.
When using $soapClient->__soapCall() the second parameter takes an array, and your data structure is also an array, so you maybe should be doing:
$params = array('customer' => $get_users_company->custcode_code);
$response = $soapClient->__soapCall("GetProducts", array($params));
Or just leave it as:
$response = $soapClient->GetProducts($params);
Which looks nicer.

List of SOAP Function in Non-WSDL Mode

I am using the following code to read attendance data from a biometric device:
<?php
$options = array(
'location' => 'http://192.168.1.178/iWsService',
'uri' => 'http://www.zksoftware/Service/message/'
);
$client = new SoapClient(null, $options);
$soapRequest = "<GetAttLog><ArgComKey xsi:type=\"xsd:integer\">0</ArgComKey><Arg><PIN xsi:type=\"xsd:integer\">All</PIN></Arg></GetAttLog>";
$response = $client->__doRequest($soapRequest, 'http://192.168.1.178/iWsService', '', '1.1');
echo '<pre>', var_dump(htmlspecialchars($response, ENT_QUOTES)), '</pre>';
?>
This just works fine. But since we do not have the documentation/manual/API Reference for this particular device (and nowhere available), I have no clue on what other functions are available in this machine.
Is there any luck finding out what other SOAP parameters this device could accept?
Your help would be really valuable at this moment. Thank you!
At last I found the SOAP SDK Manual of zksoftware.
Since I was searching for the manual for almost a year, I am uploading it to my server for the benefit of future users:
http://www.myfurni.com/downloads/zksoftware_SOAPSDKManual.pdf
Edit:
The above link doesn't work anymore. Here is the new one:
https://drive.google.com/file/d/0ByvozREXZpckcHlHYnZoOTMtWjg/view?usp=sharing
I don't think it is possible. WSDL is the source of information about functions and arguments. No WSDL - no function list.
Try this It uses the UDP protocol to communicate to the Attendance Machine.

Specifying Parameters in Zend_GData when using an Oauth Token?

So, I figured out how to get an access token from Google using the Zend_Oauth library in 1.10. Now lets say I want to get my contacts...
$config = array(
'consumerKey' => 'zzz',
'signatureMethod' => 'HMAC-SHA1',
'consumerSecret' => 'xxx'
);
$token = unserialize($_SESSION['GOOGLE_ACCESS_TOKEN']);
$client = $token->getHttpClient($config);
$client->setMethod(Zend_Http_Client::GET);
// $client->setParameterGet('max-results', '10000');
$gdata = new Zend_Gdata($client);
$gdata->setMajorProtocolVersion(3);
$query = new Zend_Gdata_Query('http://www.google.com/m8/feeds/contacts/default/full');
// $query->MaxResults=100;
$feed = $gdata->getFeed($query);
$feed is a lovely object with 25 contacts. But if I want to get more in a single pull, there doesn't seem to be a way of specifying max results that works.
If I uncomment client->setParameterGet it's ignored. It works if I specify $client->setUri and use $rawdata = client->request() to get the response, but then other issues crop up with handling the feed data that comes back... like getting it into GData for easy handling.
I've tried $feed = $gdata->importString($rawdata->getBody()) but while $rawdata->getBody() returns what seems to be valid XML, $feed->totalResults throws an error, while it wouldn't if I used $gdata->getFeed($query).
If I uncomment $query->MaxResults=100; use $gdata->getFeed($query) Google returns a 401 with "Unknown authorization header".
So is it possibly to specify parameters while using Zend_GData with an Oauth token? Or am I going to have to build my own requests, then use Zend_Feed (or some other XML/Feed dissector) for parsing?
I totally cannot get the whole list of contacts only 25... parameters do not seem to work using Gdata and query like this:
$http = $token->getHttpClient($oauthOptions);
$gdata = new Zend_Gdata($http, 'MY APP');
$gdata->setMajorProtocolVersion(3);
$gdata->getHttpClient()->setRequestScheme(Zend_Oauth::REQUEST_SCHEME_QUERYSTRING);
$query = new Zend_Gdata_Query('http://www.google.com/m8/feeds/contacts/default/full?max-results=10');
$query->setMaxResults(10);
$query->maxResults = 10;
$feed = $gdata->getFeed($query);
so i;m really into finding answers here as well. If either of you gets anything working. please post :-)
thanks
It's a bit tricky mixing a process meant to work with AuthSub with OAuth. I did some digging. So far I can get it to download all my contacts like this...
$client = $token->getHttpClient($config);
$client->setMethod(Zend_Http_Client::GET);
$client->setUri('http://www.google.com/m8/feeds/contacts/default/full/');
$client->setParameterGet('max-results', '10000');
$client->setParameterGet('v','3');
$bfeed = $client->request();
Looks like the primary difference between us is I specify the Feed URL in the $client->setUri('http://www.google.com/m8/feeds/contacts/default/full/'); and set my version differently. But I can get the body() property of $bfeed and it gives me 245k of XML to dissect.
My problem is that when I'm pulling down a single contact's feed via this method, I was getting an error.
I, like you, am trying to figure this out, so please reply with anything that works for you.

Categories