CodeIgniter REST API Library Ajax PUT throwing 403 Forbidden - php

I got the rest of the library working fully, just trying to generate api keys and its throwing a 403 forbidden when executed via ajax.
({"status":false,"error":"Invalid API Key."})
I traced it to _remap function under REST_Controller.. almost as if im calling the url incorrectly?
workflow: user visits site1.com -> registers for account -> generates api key for their domain -> key recorded in db -> key displayed
The following form would be on site1.com after they register for an account they would click "generate key".
ajax call:
/**
* Generate an API Key for Us to use
*/
$("#submitGetApiKey").click(function(){
$.ajax({
url: "http://dev.site1.com/api/key",
crossDomain: true,
type: "PUT",
dataType: "jsonp",
error: function(XMLHttpRequest, textStatus, errorThrown){
alert(errorThrown);
},
success: function(data){
for (var i = keys.length - 1; i >= 0; i--) {
console.log(keys[i]);
};
}
});
});
REST-SERVER on GitHub: https://github.com/philsturgeon/codeigniter-restserver
look specifically at key.php under application/controllers/api/key.php
Snippet of the key.php file that should relate to this process:
/**
* Key Create
*
* Insert a key into the database.
*
* #access public
* #return void
*/
public function index_put()
{
// Build a new key
$key = self::_generate_key();
// If no key level provided, give them a rubbish one
$level = $this->put('level') ? $this->put('level') : 1;
$ignore_limits = $this->put('ignore_limits') ? $this->put('ignore_limits') : 1;
// Insert the new key
if (self::_insert_key($key, array('level' => $level, 'ignore_limits' => $ignore_limits)))
{
$this->response(array('status' => 1, 'key' => $key), 201); // 201 = Created
}
else
{
$this->response(array('status' => 0, 'error' => 'Could not save the key.'), 500); // 500 = Internal Server Error
}
}
Response/Request Headers
Request URL:http://dev.mapitusa.com/api/key
Request Method:PUT
Status Code:403 Forbidden
Request Headersview source
Accept:application/json, text/javascript, */*; q=0.01
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:0
Cookie:ci_session=a%3A4%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%22e165df34aa4fda5936e940658030f83d%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A9%3A%22127.0.0.1%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A118%3A%22Mozilla%2F5.0+%28Macintosh%3B+Intel+Mac+OS+X+10_7_3%29+AppleWebKit%2F535.19+%28KHTML%2C+like+Gecko%29+Chrome%2F18.0.1025.3+Safari%2F535.19%22%3Bs%3A13%3A%22last_activity%22%3Bi%3A1328291821%3B%7Dac0f163b112dbd3769e67f4bb7122db2
Host:dev.mapitusa.com
Origin:http://dev.mapitusa.com
Referer:http://dev.mapitusa.com/api_test.html
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.3 Safari/535.19
Response Headersview source
Cache-Control:max-age=0, public
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:69
Content-Type:application/json
Date:Fri, 03 Feb 2012 18:03:54 GMT
Expires:Fri, 03 Feb 2012 18:03:54 GMT
Keep-Alive:timeout=5, max=98
Server:Apache
Set-Cookie:ci_session=a%3A4%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%22f2f466f7b97b89f2a9b557d2d9a0dbcc%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A9%3A%22127.0.0.1%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A118%3A%22Mozilla%2F5.0+%28Macintosh%3B+Intel+Mac+OS+X+10_7_3%29+AppleWebKit%2F535.19+%28KHTML%2C+like+Gecko%29+Chrome%2F18.0.1025.3+Safari%2F535.19%22%3Bs%3A13%3A%22last_activity%22%3Bi%3A1328292234%3B%7D6821b96c7e58b55f1767eb265ffdb79e; expires=Fri, 03-Feb-2012 20:03:54 GMT; path=/
Status:403
Vary:Accept-Encoding,User-Agent
X-Powered-By:PHP/5.3.6
X-UA-Compatible:IE=Edge,chrome=1

i ended up finding out the 403 forbidden was because i was not providing an api key to generate keys..
Kind of abiguous as Phil's documentation doesn't state that an existing api key is required before you can generate keys..
I simply created a bogus key in the table in the db and referenced that when calling /key/index?X-API-KEY=boguskey

I have solved the problem of generating the api key.
I'm using Phil Sturgeon's REST API server.
Call the key controller using ajax call as such :
$("#submitGetApiKey").click(function(){
$.ajax({
url: "http://sitename.com/api/key/index?X-API-KEY=your_key_here",
crossDomain: true, /* remove this if using the same domain*/
type: "PUT",
dataType: "jsonp",
error: function(XMLHttpRequest, textStatus, errorThrown){
alert(errorThrown);
},
success: function(data){
for (var i = keys.length - 1; i >= 0; i--) {
console.log(keys[i]);
};
}
});
});
Inside key controller:
Search for function _generate_key() and check for $this->load->helper('security');. the security helper must be loaded for working of do_hash otherwise you will get 500 internal server error.
public function index_put()
{
// Build a new key
$key = self::_generate_key();
// If no key level provided, give them a rubbish one
$level = $this->put('level') ? $this->put('level') : 1;
$ignore_limits = $this->put('ignore_limits') ? $this->put('ignore_limits') : 1;
// Insert the new key
if (self::_insert_key($key, array('level' => $level, 'ignore_limits' => $ignore_limits)))
{
$this->response(array('status' => 1, 'key' => $key), 201); // 201 = Created
}
else
{
$this->response(array('status' => 0, 'error' => 'Could not save the key.'), 500); // 500 = Internal Server Error
}
}
Also, you may call http://sitename.com/api/keyindex?X-API-KEY=your_key_here in your browser's address bar by making a small change in your key controller
you can replace the function name index_put with index_get.
Thanks

If you are calling this from a different domain, you may be running into some XSS issues. You might have to run it from your own server and call the function from it's own domain or possibly use the JSONP capability.
UPDATE: Are you able to see the transaction in Firebug using the NET Tab?
Do you get JSON Back?
Sometimes you have to add callback=? to the url request:
http://dev.site1.com/api/key?callback=?
Update2: Are you able to bring the page up in the browser: (http://dev.mapitusa.com/api/key)
If you get the same error, you should try giving 777 (full read/write) permissions to the site.

This sounds like it might be a browser issue. Maybe an incorrect implementation of PUT in the XMLHttpRequest stack.
I would try converting it quickly to POST just to see if it works. It might be better off leaving it as POST anyway just for compatibility purposes.

Related

Angular $http always returning null data from PHP API

I am trying to use a login API that one of my developers created for me.
$http({
method: 'POST',
url: "http://example.com/api/userLogin",
data: $.param(postLoginData),
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
responseType: 'json'
}).then(function(loginData) {
console.log(loginData);
});
My console.log() always logs the following:
Object {data: null, status: 200, config: Object, statusText: "OK"}
Yet when I go to the Network tab within developer tools, I can see that the response is actually the following:
I'm not a backend developer, so I was a little confused with this 0 at the beginning of the response. I then tried to investigate this further and looked into the PHP API code itself, and found that the response from curl_exec($loginCurl) that is returned is:
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
Date: Mon, 09 May 2016 02:10:35 GMT
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.14
Cache-Control: no-cache
Content-Length: 43
Content-Type: application/json
{"id":"16","username":"user4","status":200}
The body of the response is valid JSON, so not too sure why Angular is returning null data, even though I can see the response in developer tools successfully...
EDIT:
My API contained the following to perform checks and create sessions:
$jsonResults = json_decode($body, true);
if($jsonResults != NULL || $jsonResults->id != NULL) {
//Successfully logged in
$_SESSION['user_name'] = $jsonResults->username;
$_SESSION['user_login_status'] = 1;
$_SESSION['user_id'] = $jsonResults->id;
$this->response($body, 200);
}
Although, if the entire above code is just replaced with the following, it seems to work perfectly fine:
$this->response($body, 200);
Why would this be?
The problem is definitely server-side however you can work around it using a response transformer. For example...
$http.post('http://example.com/api/userLogin', $.param(postLoginData), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
transformResponse: function(data) {
console.log('Raw data', data);
if (angular.isString(data) && data[0] === '0') {
data = data.substring(1);
}
return angular.fromJson(data);
}
}).then(function(response) {
console.log(response);
})

How to return JSON in a Symfony2 Controller

I'm working on a PHP application that was developed a while ago and is running fine in production. However, now that I've pulled the code down and am running it on my local machine using MAMP, some things are not working. I've narrowed many of my problems down to the Symfony2, v 2.2.0 controller. When I console.log the output of the ajax that is calling my controller, I get a plain string version of the JSON. If I go into the ajax and add the parameter dataType: 'json', then the string is turned into a JSON object and my code works fine. But, if you look at the controller, shouldn't JSON be the output at that level? Can you help me understand what the problem is? I suspect is has something to do with using a newer version of PHP or something like that.
Tech:
PHP Version 5.5.3
Silex
Symfony2 Version 2.2.0
Controller
$dashboard->post('/add-food', function(Request $request) use ($app) {
$user = UserUtils::getUser($app, $app['session']->get('userId'));
$data = array(
'foodTypeId' => $request->request->get('foodTypeId'),
'addFoodAmount' => $request->request->get('foodAmount'),
'goal' => $user->getGoal(),
'people' => mround($user->getAdults() + ($user->getChildren() / 2), 1),
);
$retValue = StorageUtils::updateFoodType($app, $data, 'add');
$foodType = $retValue['foodType'];
$result = array(
'foodTypeId' => $foodType->getId(),
'userHas' => $foodType->getUserHas(),
'categoryPerc' => StorageUtils::updateFoodCategoryPerc($app, $foodType->getFoodCategory()->getId()),
'categoryId' => $foodType->getFoodCategory()->getId(),
'totalPerc' => StorageUtils::getTotalFoodPerc($app, $user->getId()),
'successMsg' => $retValue['successMsg'],
);
if ($result) {
return $app->json($result, 200, array('Content-Type' => 'application/json'));
} else {
return new Response('failure', 200);
}
});
Ajax
$.ajax({
type: "POST",
url: "/app/dashboard/add-food",
contentType: "application/json",
data: JSON.stringify(formData),
success: function(data) {
$(window).colorbox.close();
updatePage(data);
}
});
When I look at the header I get the following:
Connection: Keep-Alive
Content-Type: text/html
Date: 2014 Feb 26 12:49:01
Keep-Alive: timeout=5, max=100
Server: Apache/2.2.25 (Unix) mod_ssl/2.2.25 OpenSSL/0.9.8y DAV/2 PHP/5.5.3
Transfer-Encoding: chunked
X-Powered-By: PHP/5.5.3
Output:
{"foodTypeId":316342,"userHas":39,"categoryPerc":74.2,"categoryId":61659,"totalPerc":11.5,"successMsg":"1 gals of Vegetable Oil has been added."}
Thank you,
Aaron

CakePHP AJAX request not recognized

I'm trying to create a favorite/star/heart button (like in Gmail) for each item on a list of elements in CakePHP 2.2.
I've got a working controller and a JS toggle function but my problem is that the AJAX parts of my scripts aren't actually happening; instead the page redirects to itself (as part of the fallback for browsers with JavaScript off) and the reloads. This is what I'm trying to avoid so the users don't loose their position on the page.
Here's my CakePHP controller that Un-Favorites an item:
class MarksController extends AppController {
public $components = array('RequestHandler');
public function unfav($id = null) {
// set default class & message for setFlash
$class = 'flash_bad';
$msg = 'Invalid List Id';
if ( validation($id) ) {
//load this mark's data
$this->request->data = $this->Mark->read(null, $id);
$this->request->data['Mark']['favorite'] = '0';
if ($this->Mark->save($this->request->data)) {
$class = 'message';
$msg = 'The WatchMark has removed from favorites.';
} else {
$msg = 'This WatchMark could not be saved. Please, try again.';
}
$this->autoRender= false;
$this->autoRender = $this->layout = false;
// output JSON on AJAX request
if($this->request->is('ajax')) {
echo json_encode(array('success'=>($class=='flash_bad') ? FALSE : TRUE,'msg'=>"{$msg}"));
exit;
}
// set flash message & redirect
$this->Session->setFlash($msg,'default',array('class'=>$class));
$this->redirect($this->referer());
} else {
//trying to edit a mark that doesn't belong to this user:
throw new NotFoundException(__('Invalid mark'));
} // end ownedby check
} //end markfav method
I have another class that's the inverse function called markfav (combining them into a single toggle function is a task for another day).
Then, on the JQuery & JavaScript side:
$(document).ready(function() {
// class exists
if($('.fav_toggler').length) {
// add click handler
$('.fav_toggler').click(function(e){
$('#flashMessage').fadeOut();
if($(this).hasClass("heart")) {
$.post('/marks/markfav/' + $(this).attr('target_id'), "ajax=1", function(response) {
if(response.success == true) {
$(this).addClass("heart").removeClass("hearttoggle");
}
},'json');
}
else {
$.post('/marks/unfav/' + $(this).attr('target_id'), "ajax=1", function(response) {
if(response.success == true) {
$(this).addClass("heart").removeClass("hearttoggle");
}
},'json');
}
e.preventDefault();
});
return false;
}
});
I've tested by throwing alerts into my JavaScript and I think that it's executing correctly (it gets to the right part of my code and changes the class between heart & hearttoggle.
I also know that it's skipping the if($this->request->is('ajax')) block in my PHP for some reason (though I have an AJAX delete function that hits a similar conditional just fine). I've tested removing the if($this->request->is('ajax')) condition from the PHP block to force it to execute the JSON_encode bit and I just end up with a page containing the JSON encoded array and nothing else.
How do I make this work correctly using AJAX without a page reload?
EDIT: Headers from call to /marks/unfav/624
Request URL:http://towatchlist.com/marks/unfav/624
Request Method:GET
Status Code:200 OK
Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Cookie:CAKEPHP=blah
Host:towatchlist.com
Referer:http://towatchlist.com/marks
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_1) AppleWebKit/537.8 (KHTML, like Gecko) Chrome/23.0.1255.0 Safari/537.8
Response Headers
Connection:Keep-Alive
Content-Encoding:gzip
Content-Type:text/html
Date:Sun, 09 Sep 2012 20:55:18 GMT
Keep-Alive:timeout=5, max=94
Server:Apache/2.2.22 (Ubuntu)
Transfer-Encoding:chunked
Vary:Accept-Encoding
X-Powered-By:PHP/5.3.10-1ubuntu3.2
For some reason X-Requested-With is not presented. It should show this in your request header:
X-Requested-With: XMLHttpRequest
Here are some questions that may help:
Jquery, No X-Requested-With=XMLHttpRequest in ajax request header?
X-Requested-With header not set in jquery ajaxForm plugin
Is the AJAX request that is working being called the same way that this one?

PHP function returning 'null' for success method in jQuery.ajax call

I have an issue very similar to this one: jquery json function returning null.
I have followed the above contained advice, however, and am still seeing null as a result.
Here is my code:
JS:
Gallery.prototype.getImages = function(opt){
var self = this;
var baseurl = 'http://local.gallery.dev'
$.ajax({
url: baseurl+='/controllers/ajax.php',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: {action : opt},
dataType: 'JSON',
success: function(data){
//self.setImages(data);
console.log(data)
},
error: function(){
console.log('NOPE');
}
});
}
PHP:
class ajax_controller {
function __construct(){
if(isset($_POST['action']) && !empty($_POST['action'])) {
$action = $_POST['action'];
switch($action) {
case 'Newborn' : $this->Newborn();
break;
}
}
}
/*
* Process Newborn Gallery request.
*/
public function Newborn(){
header('Content-type: application/json');
echo json_encode(array(
'images/gallery/albums/newborn/kylie/thumbnail/kylie-album.jpg',
'images/gallery/albums/newborn/payton/thumbnail/payton-1-thumbnail.png'
));
}
}
The console/debugger/network panel are all saying that I am talking to the ajax controller correctly, however, the data of the success method only returns null.
I am fairly novice to PHP, any suggestions greatly appreciated.
Thanks, Ken
UPDATE
My call is still returning null so I thought i'd paste my headers here.
Request URL:http://local.sunnyrose.dev/controllers/ajax.php
Request Method:POST
Status Code:200 OK
Request Headersview source
Accept:application/json, text/javascript, */*; q=0.01
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:14
Content-Type:application/json; charset=UTF-8
Host:local.sunnyrose.dev
Origin:http://local.sunnyrose.dev
Referer:http://local.sunnyrose.dev/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.11 (KHTML,
like Gecko) Chrome/17.0.963.56 Safari/535.11
X-Requested-With:XMLHttpRequest
Request Payload
action=Newborn
Response Headersview source
Connection:Keep-Alive
Content-Length:0
Content-Type:application/json
Date:Mon, 05 Mar 2012 17:49:53 GMT
Keep-Alive:timeout=5, max=92
Server:Apache/2.2.14 (Unix) DAV/2 mod_ssl/2.2.14 OpenSSL/0.9.8l PHP/5.3.1 mod_perl/2.0.4
Perl/v5.10.1
X-Powered-By:PHP/5.3.1
echo at the end of constructor.i think you doesnt echo anything in controller , so ajax response is null.
whioch framework u use?
I dont think thats going to produce valid JSON. If you want an actual array with numeric keys then use numeric keys in the PHP array:
echo json_encode(array(
1 => 'images/gallery/albums/newborn/kylie/thumbnail/kylie-album.jpg',
2 => 'images/gallery/albums/newborn/payton/thumbnail/payton-1-thumbnail.png'
));
OR
echo json_encode(array(
'images/gallery/albums/newborn/kylie/thumbnail/kylie-album.jpg',
'images/gallery/albums/newborn/payton/thumbnail/payton-1-thumbnail.png'
));
which will output the following js:
[
'images/gallery/albums/newborn/kylie/thumbnail/kylie-album.jpg',
'images/gallery/albums/newborn/payton/thumbnail/payton-1-thumbnail.png'
]
In your /controllers/ajax.php file are you running your functions?
class ajax_controller {
function __construct(){
if(isset($_POST['action']) && !empty($_POST['action'])) {
$action = $_POST['action'];
switch($action) {
case 'Newborn' : return $this->Newborn();
break;
}
}
}
/*
* Process Newborn Gallery request.
*/
public function Newborn(){
header('Content-type: application/json');
return json_encode(array(
'images/gallery/albums/newborn/kylie/thumbnail/kylie-album.jpg',
'images/gallery/albums/newborn/payton/thumbnail/payton-1-thumbnail.png'
));
}
}
$controller = new ajax_controller;
echo $controller->__construct();
you are using part of a url
url: '/controllers/ajax.php',
try using your full url
like
var baseurl = 'http://www.example.com';
url: baseurl+'/controllers/ajax.php',
EDIT
try changing
header('Content-type: application/json')
to
header('Content-type: text/json');
or
header('Content-type: text/plain');
https://stackoverflow.com/questions/267546/correct-http-header-for-json-file
I finally dug around the headers and realized the problem was that .ajax() was sending my PHP script an empty array.
The fix was to get rid of contentType: 'application/json; charset=utf-8, and the header() function in the PHP script.
It now sends and receives data just fine. (goes to read up on configuration docs)
Thanks for all the help - my script would;ve been broken for other reasons w/o many of your answers :)

Check if the index exists or not Elasticsearch

I want to check in elasticsearch if the index exists or not. If it not exists it should create the index and do other functionality. I try to find out a solution for that, but did not find any perfect solution for that. Can anyone have any solution to solve this problem.
I am using Elasticsearch library.
**$client = new Elasticsearch\Client();**
As per index operations and source code the following should work
$client = new Elasticsearch\Client();
$indexParams['index'] = 'my_index';
$client->indices()->exists($indexParams);
This will return true or false:
$params = ['index' => 'products'];
$bool=$client->indices()->exists($params);
The documentation for list all indexes here: https://www.elastic.co/guide/en/elasticsearch/reference/current/_list_all_indexes.html
Using curl:
curl 'localhost:9200/_cat/indices?v'
Other way by using Facade:
use ScoutElastic\Facades\ElasticClient;
$indexParams['index'] = "model_index";
$exists = ElasticClient::indices()->exists($indexParams);
if ($exists) {
//do somthing
}
For more recent versions of Elasticsearch (8.x), using the php library (with corresponding version 8.x) a call to $client->indices()->exists($indexParams) no longer returns a boolean, it instead returns an instance of Elastic\Elasticsearch\Response\Elasticsearch. This response is actually a 200 HTTP response if the index exists, or a HTTP 404 if it does not, but it also has the helpful asBool() you can use that abstracts away the HTTP codes e.g.
$client->indices()->exists($indexParams)->asBool();
I was able to do it with node.js, as below
const { Client } = require('#elastic/elasticsearch');
const client = new Client({
node: ES_URL,
});
await client.indices.exists({ index: 'INDEX_NAME' });
and the response should be similar to this one below:
{
body: true,
statusCode: 200,
headers: {
date: 'Sun, 07 Mar 2021 13:07:31 GMT',
server: 'Apache/2.4.46 (Unix) OpenSSL/1.1.1d',
'content-type': 'application/json; charset=UTF-8',
'content-length': '2796',
'keep-alive': 'timeout=5, max=100',
connection: 'Keep-Alive'
},
meta: {
context: null,
request: { params: [Object], options: {}, id: 1 },
name: 'elasticsearch-js',
connection: {
url: 'ES_URL',
id: 'ES_ID',
headers: {},
deadCount: 0,
resurrectTimeout: 0,
_openRequests: 0,
status: 'alive',
roles: [Object]
},
attempts: 0,
aborted: false
}
}

Categories