PHP NTLM authentication + soap client - php

I came across the project where I need to implement web services protected by NTLM authentication.
I gave it try via PHP SoapClient:
$client = new \SoapClient("http://hostname.com/webservice",
array(
'cache_wsdl' => WSDL_CACHE_NONE,
'login' => "username",
'password' => "password"
));
This throws this error:
PHP Fatal error: SOAP-ERROR: Parsing WSDL: Couldn't load from
'http://hostname.com/webservice' : failed to load external entity
"http://hostname.com/webservice"
I gave it try via CURL:
curl --ntlm -u username:password "http://hostname.com/webservice" --verbose
It works as expected and returned the correct xml.
But again SoapClient doesn't support NTLM authentication.
Does anyone knows anything about PHP Soap+NTLM?
Thanks in advance

I recently had this problem too.
Here's the solution I found:
Create NTLMStream.php with the following contents:
<?php
/*
* Original https://thomas.rabaix.net/blog/2008/03/using-soap-php-with-ntlm-authentication
* Modified by http://blogs.msdn.com/b/freddyk/archive/2010/01/19/connecting-to-nav-web-services-from-php.aspx:
*/
class NTLMStream
{
private $path;
private $mode;
private $options;
private $opened_path;
private $buffer;
private $pos;
public function stream_open($path, $mode, $options, $opened_path) {
$this->path = $path;
$this->mode = $mode;
$this->options = $options;
$this->opened_path = $opened_path;
$this->createBuffer($path);
return true;
}
public function stream_close() {
curl_close($this->ch);
}
public function stream_read($count) {
if(strlen($this->buffer) == 0) {
return false;
}
$read = substr($this->buffer,$this->pos, $count);
$this->pos += $count;
return $read;
}
public function stream_write($data) {
if(strlen($this->buffer) == 0) {
return false;
}
return true;
}
public function stream_eof() {
return ($this->pos > strlen($this->buffer));
}
public function stream_tell() {
return $this->pos;
}
public function stream_flush() {
$this->buffer = null;
$this->pos = null;
}
public function stream_stat() {
$this->createBuffer($this->path);
$stat = array(
'size' => strlen($this->buffer),
);
return $stat;
}
public function url_stat($path, $flags) {
$this->createBuffer($path);
$stat = array(
'size' => strlen($this->buffer),
);
return $stat;
}
private function createBuffer($path) {
if($this->buffer) {
return;
}
$this->ch = curl_init($path);
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($this->ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($this->ch, CURLOPT_USERPWD, USERPWD);
$this->buffer = curl_exec($this->ch);
$this->pos = 0;
}
}
class NTLMSoapClient extends \SoapClient
{
function __doRequest($request, $location, $action, $version, $one_way = 0) {
$headers = array(
'Method: POST',
'Connection: Keep-Alive',
'User-Agent: PHP-SOAP-CURL',
'Content-Type: text/xml; charset=utf-8',
'SOAPAction: "'.$action.'"',
);
$this->__last_request_headers = $headers;
$ch = curl_init($location);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, true );
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_USERPWD, USERPWD);
$response = curl_exec($ch);
return $response;
}
function __getLastRequestHeaders() {
return implode("\n", $this->__last_request_headers)."\n";
}
}
Then in your test.php
<?php
define('USERPWD', 'domain\username:password');
require_once("NTLMStream.php");
stream_wrapper_unregister("https");
stream_wrapper_register("https", "NTLMStream");
$params = [
'stream_context' => stream_context_create([
'ssl' => [
'ciphers'=>'RC4-SHA',
'verify_peer'=>false,
'verify_peer_name'=>false,
'allow_self_signed'=>true,
]]),
'cache_wsdl' => WSDL_CACHE_NONE,
'soap_version' => SOAP_1_1,
'trace' => 1,
'connection_timeout' => 180,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS
];
$client = new NTLMSoapClient("https://hostname.com/webservice", $params);
$retVal = $client->ReadMultiple(...
...
For http:// instead of https:// substitute:
stream_wrapper_register("http", "NTLMStream")
stream_wrapper_register("https", "NTLMStream")

This library helped me (I use composer):
https://packagist.org/packages/matejsvajger/ntlm-soap-client
$url = 'URL_TO_WEBSERVICE_WSDL';
$config = new matejsvajger\NTLMSoap\Common\NTLMConfig([
'domain' => 'domain',
'username' => 'username',
'password' => 'password'
]);
$client = new matejsvajger\NTLMSoap\Client($url, $config);
$response = $client->ReadMultiple(['filter'=>[], 'setSize'=>1]);
foreach ($response->ReadMultiple_Result->CRMContactlist as $entity) {
print_r($entity);
}

Related

Laravel: My method is not returning curl result, returns pased parameters instead [duplicate]

This question already has answers here:
Constructor returning value?
(6 answers)
Closed 10 months ago.
In my laravel app I use curl to send notifications via FCM (Firebase Cloud Messaging) rest API using curl and php, when I execute curl I save result to result variable along with http code and return it, however what I receive in $notification variable is the parametters passed to NewMatchMotif instead of curl result:
$notification = new NewMatchNotif($user, $data, 'es');
Something like this:
{user: {…}, data: {…}, locale: "es"}
My NewMatch Notification:
<?php
namespace App\Notifications;
use App\Helpers\CloudMessaging;
use Lang;
use Log;
class NewMatchNotif
{
public $user;
public $data;
public $locale;
public function __construct($user, $data, $locale)
{
$this->user = $user;
$this->data = $data;
$this->locale = $locale;
$this->load();
}
public function load()
{
$cloudMessaging = new CloudMessaging();
$result = $cloudMessaging->send($this->user, 'title', 'body', $this->data, 'NewMatchNotif');
return $result;
}
}
My send method
public function send($user,$title,$body, $data = false , $type, $image='')
{
$fields = '...';
$url = 'https://fcm.googleapis.com/fcm/send';
$fields = json_encode($fields);
$headers =
[
'Authorization: key='. config('services.firebase.api_key'),
'Content-Type: application/json',
];
$ch = curl_init ();
curl_setopt ( $ch, CURLOPT_URL, $url );
curl_setopt ( $ch, CURLOPT_POST, true );
curl_setopt ( $ch, CURLOPT_HTTPHEADER, $headers );
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt ( $ch, CURLOPT_POSTFIELDS, $fields );
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY , true);
$result = curl_exec ( $ch );
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close ( $ch );
$output =
[
'httpCode' => $httpcode,
'result' => $result
];
return $output;
}
What is going on, this part is pretty straight fordward yet I'm not getting the desired results:
$result = curl_exec ( $ch );
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close ( $ch );
$output =
[
'httpCode' => $httpcode,
'result' => $result
];
return $output;
you need to get the result after instanciate the class, like:
<?php
namespace App\Notifications;
use App\Helpers\CloudMessaging;
use Lang;
use Log;
class NewMatchNotif
{
public $user;
public $data;
public $locale;
public $result;
public function __construct($user, $data, $locale)
{
$this->user = $user;
$this->data = $data;
$this->locale = $locale;
$this->load();
}
private function load()
{
$cloudMessaging = new CloudMessaging();
$this->result = $cloudMessaging->send($this->user, 'title', 'body', $this->data, 'NewMatchNotif');
}
}
And then:
$notification = (new NewMatchNotif($user, $data, 'es'))->result;

Soundcloud API (PHP) returns "invalid grant"

We are using the code below to try to upload tracks from our WordPress site to SoundCloud through PHP. MP3 tracks are already on our server before running this script.
function upload_soundcloud()
{
//App credentials. Create one at http://soundcloud.com/you/apps
define('API_URL', 'https://api.soundcloud.com');
define('CLIENT_ID', '$client_id');
define('CLIENT_SECRET', '$client_secret');
//User credentials
define('EMAIL', '$email');
define('PASSWORD', '$password');
//Path to MP3 file to upload
define('FILE', ABSPATH . '/wp-content/uploads/path/to/file.mp3');
class SoundcloudAPI {
private $url;
private $clientID;
private $secret;
private $accessToken;
public function __construct($url, $clientID, $secret) {
$this->url = $url;
$this->clientID = $clientID;
$this->secret = $secret;
}
public function auth($username, $password) {
$url = $this->url . '/oauth2/token';
$data = array(
'client_id' => $this->clientID,
'client_secret' => $this->secret,
'grant_type' => 'password',
'username' => $username,
'password' => $password
);
$result = $this->request($url, $data, 'POST');
$this->accessToken = $result->access_token;
return $result;
}
public function upload($title, $path) {
$url = $this->url . '/tracks';
$data = array(
'oauth_token' => $this->accessToken,
'track[title]' => $title,
'track[asset_data]' => new CurlFile(realpath($path), 'audio/mpeg'),
);
$result = $this->request($url, $data, 'POST');
return $result;
}
private function request($url, $data, $method) {
$curl = curl_init();
if ($method === 'POST') {
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
} else {
$url .= '?' . http_build_query($data);
}
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
$result = json_decode(curl_exec($curl));
curl_close($curl);
return $result;
}
}
$soundCloud = new SoundcloudAPI(API_URL, CLIENT_ID, CLIENT_SECRET);
$resultAuth = $soundCloud->auth(EMAIL, PASSWORD);
$resultUpload = $soundCloud->upload('Test', FILE);
echo '<pre>';
print_r($resultAuth);
print_r($resultUpload);
echo '</pre>';
}
This is what we are getting as a response:
stdClass Object
(
[error] => invalid_grant
)
Does anyone know why this might be happening? (the strangest part is that it was actually working 2 days ago, but then just stopped working and I feel like I am going crazy).
Try and save the access token and refresh token between requests (for example, in a local file or database) and reusing it. The rate-limit only seems to be on the /oauth2/token endpoint when using a username and password.
Once your access token has expired, you can use the refresh token to get a new one by making a request to /oauth2/token with:
'grant_type': 'refresh_token',
'client_id': <your client id>,
'client_secret': <your client secret>,
'refresh_token': <previous refresh token>

cURL PUT/PATCH request with Discord API resulting in error 400 (BAD REQUEST)

I am using php, oAuth2 (from Discord) and cURL to connect to the Discord API, where after the user authorizes the connection, he will automatically enter the server (guild) with a specific nickname and role, but if the user is already on the server, just changes the nickname and adds the role automatically.
I've had success with Auth2, so I'm looking at it. He returns everything I wanted, using identy, guilds.join and email scopes. However, in the process of adding the member to the server or editing the member, it returns error 400 (BAD REQUEST). And I really don't have a clue what it is, so what I'm seeing seems to be all right.
The strange thing is that yesterday, it was working only in the anonymous browser tab, but today, it even worked in my normal browser, but for other users, it does not work.
To tell you the truth, I've already got error 400 and 403, it's really very confusing.
My complete code:
class Discord extends CodonModule {
private static $OAUTH2_CLIENT_ID = 'CLIENT_ID';
private static $OAUTH2_CLIENT_SECRET = 'CLIENT_SECRET';
private static $BOT_TOKEN = 'CLIENT_TOKEN';
private static $guildID = 453922275248265175;
private static $roleID = 45129328442467690261;
public static $authorizeURL = 'https://discordapp.com/api/oauth2/authorize';
public static $tokenURL = 'https://discordapp.com/api/oauth2/token';
public static $apiURLBase = 'https://discordapp.com/api/users/#me';
public function login() {
$params = array(
'client_id' => Discord::$OAUTH2_CLIENT_ID,
'redirect_uri' => 'LINK/request',
'response_type' => 'code',
'scope' => 'identify guilds.join email'
);
// Redirect the user to Discord's authorization page
header('Location: https://discordapp.com/api/oauth2/authorize' . '?' . http_build_query($params));
die();
}
public function request() {
if($this->get('code')) {
// Exchange the auth code for a token
$token = $this->apiRequest(Discord::$tokenURL, array(
"grant_type" => "authorization_code",
'client_id' => Discord::$OAUTH2_CLIENT_ID,
'client_secret' => Discord::$OAUTH2_CLIENT_SECRET,
'redirect_uri' => 'LINK/request',
'code' => $this->get('code')
));
$logout_token = $token->access_token;
$_SESSION['access_token'] = $token->access_token;
header('Location: ' . $_SERVER['PHP_SELF']);
}
if($this->session('access_token')) {
$user = $this->apiRequest(Discord::$apiURLBase);
$userID = intval($user->id);
$userTAG = $user->username.'#'.$user->discriminator;
$newName = 'Teste';
$params = '{"access_token": "'.$_SESSION['access_token'].'", "nick": "'.$newName.'", "roles": ['.Discord::$roleID.']}';
$code = $this->membersRequest($userID, 'PUT', $params);
// The pilot is not on the guild
if($code == 201) {
$this->show('discord/discord_success.php');
return true;
} elseif($code == 204) {
// The pilot is already on the server
$params2 = '{"nick": "'.$newName.'", "roles": ['.Discord::$roleID.']}';
$http = $this->membersRequest($userID, 'PATCH', $params2);
if($http == 204) {
$this->show('discord/discord_success.php');
return true;
} else {
$this->show('discord/discord_error.php');
return false;
}
} else {
$this->show('discord/discord_error.php');
return false;
}
} else {
$this->index();
}
}
function apiRequest($url, $post=FALSE, $headers=array()) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$response = curl_exec($ch);
if($post)
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post));
$headers[] = 'Accept: application/json';
if($this->session('access_token'))
$headers[] = 'Authorization: Bearer ' . $this->session('access_token');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
return json_decode($response);
}
function membersRequest($userID, $post, $params) {
$membersURL = 'https://discordapp.com/api/guilds/'.Discord::$guildID.'/members/';
$url = $membersURL.$userID;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $post);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Authorization: Bot ' . Discord::$BOT_TOKEN,
'Content-Type: application/json'
));
curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
return $http;
}
function get($key, $default=NULL) {
return array_key_exists($key, $_GET) ? $_GET[$key] : $default;
}
function session($key, $default=NULL) {
return array_key_exists($key, $_SESSION) ? $_SESSION[$key] : $default;
}
}
Any help would be much appreciated. Thank you so much.

PHP Bind Json api Data to table

I am connecting to a API which doesnt really have a lot of documentation: I would like to bind the data returned to a table but at the moment its all not even loading...Im rather new at this... Im just not sure how to actually bind the data...Thanks!
Example response from docs:
{
"service":"1",
"name":"Custom Comments",
"type":"Custom Comments"
}
HTML
<div class="col-sm-9">
<?php $api = new Api(); ?>
<?php $services = $api->services(); # return all services ?>
</div><!-- col-sm-9 -->
API CLASS
<?php
class Api
{
public $api_url = 'http://apiurl'; // API URL
public $api_key = 'key'; // Your API key
public function order($data) { // add order
$post = array_merge(array('key' => $this->api_key, 'action' => 'add'), $data);
return json_decode($this->connect($post));
}
public function status($order_id) { // get order status
return json_decode($this->connect(array(
'key' => $this->api_key,
'action' => 'status',
'id' => $order_id
)));
}
public function services() { // get services
return json_decode($this->connect(array(
'key' => $this->api_key,
'action' => 'services',
)));
}
private function connect($post) {
$_post = Array();
if (is_array($post)) {
foreach ($post as $name => $value) {
$_post[] = $name.'='.urlencode($value);
}
}
$ch = curl_init($this->api_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
if (is_array($post)) {
curl_setopt($ch, CURLOPT_POSTFIELDS, join('&', $_post));
}
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
$result = curl_exec($ch);
if (curl_errno($ch) != 0 && empty($result)) {
$result = false;
}
curl_close($ch);
return $result;
}
}

Trying send push notification throws an exception?

I'm trying create a PHP class to send Push Notification. When I try send push doesn't works an throws an exception of syntax error and I can't find this error. I'm using PHP 5.3.28.
How could I solve it ?
Pusher
<?php
class Pusher{
const GOOGLE_GCM_URL = 'https://android.googleapis.com/gcm/send';
private $apiKey;
private $proxy;
private $output;
public function __construct($apiKey, $proxy = null)
{
$this->apiKey = $apiKey;
$this->proxy = $proxy;
}
/**
* #param string|array $regIds
* #param string $data
* #throws \Exception
*/
public function notify($regIds, $data)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, self::GOOGLE_GCM_URL);
if (!is_null($this->proxy)) {
curl_setopt($ch, CURLOPT_PROXY, $this->proxy);
}
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->getHeaders());
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, $this->getPostFields($regIds, $data));
$result = curl_exec($ch);
if ($result === false) {
throw new \Exception(curl_error($ch));
}
curl_close($ch);
$this->output = $result;
}
/**
* #return array
*/
public function getOutputAsArray()
{
return json_decode($this->output, true);
}
/**
* #return object
*/
public function getOutputAsObject()
{
return json_decode($this->output);
}
private function getHeaders(){
return [
'Authorization: key=' . $this->apiKey,
'Content-Type: application/json'
];
}
private function getPostFields($regIds, $data){
$fields = [
'registration_ids' => is_string($regIds) ? [$regIds] : $regIds,
'data' => is_string($data) ? ['message' => $data] : $data,
];
return json_encode($fields, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE);
}
}
Exception
Fatal Error
Error: syntax error, unexpected '['
File: /app/Plugin/Push/Pusher.php
Line: 60
At line 60 have
private function getHeaders(){
return [
'Authorization: key=' . $this->apiKey,
'Content-Type: application/json'
];
}

Categories