Cannot authorize PHP NTLMSoapClient with MS Dynamics Great Plains ERP - php

Having difficulties authorizing php SoapClient with MS Dynamic Great Plains. I can connect through SoapUI. However, it only successfully connects on 3rd attempt. Also, the auth token progressively gets longer. See pastebin link below.
I made use of the following package (https://github.com/mlabrum/NTLMSoap) to setup a NTLM stream, but it doesn't seem to be sending a correct token. The token length is shorter than what is sent through SoapUI.
$wsdlUrl = 'http://example.org:48620/Metadata/Legacy/Full/DynamicsGP.wsdl';
$options = [
'ntlm_username' => 'Domain\username',
'ntlm_password' => 'password'
];
$soapClient = new \NTLMSoap\Client($wsdlUrl, $options);
$params = array(
criteria => array(
'ModifiedDate' => array(
'GreaterThan' => '2016-04-18',
'LessThan' => '2016-04-19'
)
),>
'context' => array(
'OrganizationKey' => array(
'type' => 'CompanyKey',
'Id' =
)
)
);
$soapClient->__setLocation('http://example.org:48620/DynamicsGPWebServices/DynamicsGPService.asmx');
$response = $soapClient->GetPurchaseOrderList(array($params));
I had to set use ___setLocation() because client was being forwarded to http://localmachine:48620/DynamicsGPWebServices/DynamicsGPService.asmx
I have been trying to get Charles Web Proxy to work to show the actual the request/response, buts its crapped out on me.
This is the SoapUI output. http://pastebin.com/7zg4E3qD

Related

Laravel, soap API required login

I am having a little trouble with the SOAP API I need to consume. It requires authorization, so I log in with the following.
Basically, I need to log in with the following, I don't get a "Login Required", which is fine.
$client = new \SoapClient('localwsdlfile.wsdl', array(
'local_cert' => 'localcert.pem',
'passphrase' => 'passphrase',
'location' => 'https://wsmurl/login/'
));
$response = $client->Get(array(
"AccessKey" => "accesskey",
"ProductID" => "SOMEPRODUCT",
"Scope" => "SOMESCOPE",
"Parameters" => array('Param' => array('_' => 'DATATOLOOKUP', 'id'=>'MOREDATATOLOOKUP'))
));
print_r($response);
So, basically, in the client, I need to uncomment the line
'location' => 'https://wsmurl/login/'
If I don't, I get this login error. The server I am trying to consume has a cache, so I no longer need to send this login for future operations for the next 10 minutes.
So, now, I have logged in, I can go the following, with success.
$client = new \SoapClient('localwsdlfile.wsdl', array(
'local_cert' => 'localcert.pem',
'passphrase' => 'passphrase',
// 'location' => 'https://wsmurl/login/' // Uncomment to login
));
$response = $client->Get(array(
"AccessKey" => "accesskey",
"ProductID" => "SOMEPRODUCT",
"Scope" => "SOMESCOPE",
"Parameters" => array('Param' => array('_' => 'DATATOLOOKUP', 'id'=>'MOREDATATOLOOKUP'))
));
print_r($response);
If I keep 'location' => 'https://wsmurl/login/' uncommented, I get errors about not sending a valid WSDL.
So in summary:-
I am consuming this soap API in Laravel, using PHP's SoapClinet per the above code.
My understanding is that the soap service I am trying to consume required login first.
After quite a lot of testing, I managed to get to the point there were no errors by including the line 'location' => 'https://wsmurl/login/' as shown above in the call to SoapClient.
This seems to be required the first time I call this soap service I guess it is logging me in. If I don't include it, I get the error
"SoapFault
Login Required"
So, if I include the line 'location' => 'https://wsmurl/login/', it appears to log me in, but I now get the error
"SoapFault
looks like we got no XML document"
If I keep the line, I continue to get this error
"SoapFault
looks like we got no XML document"
Removing the line then gives me the expected SOAP response and all is ok.
I have tried catching for errors using try{} ... catch{} looking for exceptions/errors etc, but it isn't catching the "Login Required" or "Looks like we got no XML document" as faults.
So, I guess, is there a better way to detect this condition so I can either send or not without the line or is there a better way to login to the service (note, it just uses the cert and passphrase).
Thanks in advance, I hope the little re-write above makes it clearer.
Because the location for the login and localwsdlfile.wsdl is different, I could not do it with one call. So we created a function using curl to login and if login is successful it will proceed to the soap client. Thanks to Brian from freelancer for his assistance here.
$client = new SoapClient('wsdl/VocusSchemas/localwsdlfile.wsdl', array(
'trace' => 1,
'exception' => true
));
try {
$response = $client->Get(array(
// "AccessKey" => "MADAITABNSAATOT1",
"AccessKey" => "accesskey",
"ProductID" => "SOMEPRODUCT",
"Scope" => "SOMESCOPE",
"Parameters" => array('Param' => array('_' => 'DATATOLOOKUP', 'id' => 'MOREDATATOLOOKUP'))
));

Connecting to a SOAP service with 2 way SSL

Basically, I am trying to connect to a SOAP service with 2 way SSL (HTTP client certificate authentication).
I am using PHP SoapClient for this within Laravel.
This is what I have, and it allows me to connect and returns the expected response. So the method is basically correct and the certificate is fine etc. It's just I am having trouble with what I guess is the 2 way SSL part.
$client = new \SoapClient('localwsdlfile.wsdl', array(
'local_cert' => 'localcert.pem',
'passphrase' => 'passphrase',
// 'location' => 'https://wsmurl/login/' // Uncomment to login
));
$response = $client->Get(array(
"AccessKey" => "accesskey",
"ProductID" => "SOMEPRODUCT",
"Scope" => "SOMESCOPE",
"Parameters" => array('Param' => array('_' => 'DATATOLOOKUP', 'id'=>'MOREDATATOLOOKUP'))
));
print_r($response);
The only problem is (obviously because I'm doing something wrong), is I need to add the line 'location' => 'https://wsmurl/login/' the first time I try to connect, otherwise I get an error "SoapFault Login Required"
I then remove the line 'location' => 'https://wsmurl/login/', otherwise, I get an error "SoapFault looks like we got no XML document".
The service provider has a 600 second timeout, where I don't have to "login" again for upto 600 seconds.
After removing this line 'location' => 'https://wsmurl/login/', then it works as expected. Obviously, I can't manually add and remove this line, and I guess I am not doing this correctly.
Can someone tell me a better way to do this please?
Thanks,
Because the location for the login and localwsdlfile.wsdl is different, I could not do it with one call. So we created a function using curl to login and if login is successful it will proceed to the soapclient. Thanks to Brian from freelancer for his assistance here.
$client = new SoapClient('wsdl/VocusSchemas/localwsdlfile.wsdl', array(
'trace' => 1,
'exception' => true
));
try {
$response = $client->Get(array(
// "AccessKey" => "MADAITABNSAATOT1",
"AccessKey" => "accesskey",
"ProductID" => "SOMEPRODUCT",
"Scope" => "SOMESCOPE",
"Parameters" => array('Param' => array('_' => 'DATATOLOOKUP', 'id' => 'MOREDATATOLOOKUP'))
));

Failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request - PHP Error

SOLUTION: I had malformed my JSON data for the payload body. The "ttl" => 30 was in the incorrect array() method. This probably won't help anyone in the future, moving the ttl key/value pair made this work correctly as seen below.
$data = array(
"statement" => array(
"actor" => array(
"mbox" => "mailto:test#example.com"
),
),
"ttl" => 30
);
I have checked numerous other StackOverflow questions and cannot find a solution that works. I should note that I am testing this using a local XAMPP server running on port 8080. Not sure if that matters. I have been able to get this working using Postman, but translating it to PHP has been problematic. Am I missing something? I am not all that familiar with PHP, but need this for work.
EDIT: Some more information about what the API is expecting. It's a fairly simple API that requires a JSON body, a Basic Authorization header, and a Content-Type: application/json.
Here is the JSON body I am using in Postman. This is a direct copy/paste from Postman, which is successfully communicating with the API:
{
"statement": {
"actor": {
"mbox": "mailto:test#example.com"
}
},
"ttl": 30
}
Is there a syntax error in my below PHP code for this? Again, I am learning PHP on the fly so I'm unsure if I am properly constructing a JSON payload using the array() method in PHP.
My code below has the $https_user,$https_password, and $url domain changed for obvious security reasons. In my actual PHP code, I have the same credentials and domain used in Postman.
The $randomSessionID serves no real purpose other than an identification number for future requests. Has no affect on the API response failing or succeeding.
<?php
$https_user = 'username';
$https_password = 'password';
$randomSessionID = floor((mt_rand() / mt_getrandmax()) * 10000000);
$url = 'https://www.example.com/session/' . $randomSessionID . '/launch';
$json = json_encode(array(
"statement" => array(
"actor" => array(
"mbox" => "mailto:test#example.com"
),"ttl" => 30
)
));
$options = array(
'http' => array(
'method' => 'POST',
'header' => 'Content-Type: application/json\r\n'.
"Authorization: Basic ".base64_encode("$https_user:$https_password")."\r\n",
'content' => $json
)
);
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === FALSE) { /* Handle error */ }
?>
SOLUTION: I had malformed my JSON data for the payload body. The "ttl" => 30 was in the incorrect array() method. This probably won't help anyone in the future, but moving the ttl key/value pair made this work correctly as seen below.
$data = array(
"statement" => array(
"actor" => array(
"mbox" => "mailto:test#example.com"
),
),
"ttl" => 30
);

Doctrine 2 and OAuth2.0 Server PHP Client Credentials are invalid

I am trying to implement OAuth2 with Doctrine as an entity manager. I followed this tutorial exactly:
http://bshaffer.github.io/oauth2-server-php-docs/cookbook/doctrine2/
Here is my code that is called when a user makes a request to the API:
// obtaining the entity manager
$entityManager = EntityManager::create($conn, $config);
$clientStorage = $entityManager->getRepository('OAuthClient');
$clients = $clientStorage->findAll();
print_r($clients); // We are getting the clients from the database.
$userStorage = $entityManager->getRepository('OAuthUser');
$accessTokenStorage = $entityManager->getRepository('OAuthAccessToken');
$authorizationCodeStorage = $entityManager->getRepository('OAuthAuthorizationCode');
$refreshTokenStorage = $entityManager->getRepository('OAuthRefreshToken');
//Pass the doctrine storage objects to the OAuth2 server class
$server = new \OAuth2\Server([
'client_credentials' => $clientStorage,
'user_credentials' => $userStorage,
'access_token' => $accessTokenStorage,
'authorization_code' => $authorizationCodeStorage,
'refresh_token' => $refreshTokenStorage,
], [
'auth_code_lifetime' => 30,
'refresh_token_lifetime' => 30,
]);
$server->addGrantType(new OAuth2\GrantType\ClientCredentials($clientStorage));
// handle the request
$server->handleTokenRequest(OAuth2\Request::createFromGlobals())->send();
Whenever a call using the correct credentials is made, I get this response:
Array
(
[0] => OAuthClient Object
(
[id:OAuthClient:private] => 1
[client_identifier:OAuthClient:private] => testclient
[client_secret:OAuthClient:private] => testpass
[redirect_uri:OAuthClient:private] => http://fake.com
[hashOptions:protected] => Array
(
[cost] => 11
)
)
[1] => OAuthClient Object
(
[id:OAuthClient:private] => 2
[client_identifier:OAuthClient:private] => trevor
[client_secret:OAuthClient:private] => hutto
[redirect_uri:OAuthClient:private] => https://www.another.com
[hashOptions:protected] => Array
(
[cost] => 11
)
)
)
{"error":"invalid_client","error_description":"The client credentials are invalid"}
So we are getting the clients from the database, we should be checking them, and returning that they in fact exists and issuing an access token. However, for some reason, OAuth2 Server (can be seen here) can not match the given credentials with the stored credentials.
I do not think this is a Doctrine problem because I can retrieve the results fairly easily using findAll().
My question is:
Why is this happening, and how can I fix it?
I found the problem. In the tutorial (http://bshaffer.github.io/oauth2-server-php-docs/cookbook/doctrine2/) they fail to mention that when the client secret is checked with against a hashed version of the provided client secret.
In the tutorial they do not hash the example client secret when they put it in the database.
If you hash your client secret when inserting it into the database, it will work as expected.

Using SoapClient in php in mule

I am using the following php script in mule. When i am running this php file individually(through wamp) i am able to get the required output.
<?php
$client1=new SoapClient('example/v2_soap?wsdl', array('trace' => 1, 'connection_timeout' => 120));
$username = '******';
$password = '******';
//retreive session id from login
$session = $client1->login(
array(
'username' => $username,
'apiKey' => $password,
)
);
$result= $client1->catalogProductInfo(
array(
'sessionId' => $session->result,
'productId' => 1,
)
);
print_r($result);
return $result;
?>
But i want to run the following script through mule. So when i am running it through mule i am getting the following error.
Root Exception stack trace:
com.caucho.quercus.QuercusErrorException: eval::5: Fatal Error: 'SoapClient' is an unknown class name.
at com.caucho.quercus.env.Env.error(Env.java:4480)
at com.caucho.quercus.env.Env.error(Env.java:4399)
at com.caucho.quercus.env.Env.createErrorException(Env.java:4130)
+ 3 more (set debug level logging or '-Dmule.verbose.exceptions=true' for everything)
It says SoapClient is an unknown class. What is the problem here?
Do i have to include some SoapClient here? If so where can i find it. Please help!!
I'm not sure if mule supports php extensions, but that's what it seems by the error. You could try downloading nusoap into your project,
which doesn't require any php extension. The syntaxis is a little bit different though, but shouldn't be harder to adapt your code.
For what it's worth, this is a simple example of a soap request using nusoap (taken from here http://www.richardkmiller.com/files/msnsearch_nusoap.html):
require_once('nusoap/lib/nusoap.php');
$request = array('Request' => array(
'AppID' => 'MSN_SEARCH_API_KEY',
'Query' => 'Seinfeld',
'CultureInfo' => 'en-US',
'SafeSearch' => 'Strict',
'Flags' => '',
'Location' => '',
'Requests' => array(
'SourceRequest' => array(
'Source' => 'Web',
'Offset' => 0,
'Count' => 50,
'ResultFields' => 'All'))));
$soapClient = new soapclient("http://soap.search.msn.com/webservices.asmx?wsdl", false);
$result = $soapClient->call("Search", $request);
print_r($result);
I hope it helps.
I understand there seems to be a problem with running the soap client inside quercus (rather than Mule).
However instead of focusing on it I would suggest to take a look to the CXF client and the Web services consumer. You are running inside of Mule a powerful opensource ESB, there is no need to write a php script to consume a service, you have all that functinality already present.

Categories