CSRF token from either the request body or request headers did not match or is missing - cake php 4 [duplicate] - cakephp

I have a project in Cakephp 3.6 in which 3 actions in MessageController are called by Ajax. I have a problem, however, when I send a request to one of the action, XHR returns to me this:
{
"message": "CSRF token mismatch.",
"url": "\/messages\/changepriority\/8",
"code": 403,
"file": "D:\\xampp\\htdocs\\myapp\\vendor\\cakephp\\cakephp\\src\\Http\\Middleware\\CsrfProtectionMiddleware.php",
"line": 195
}
This is one of the action what I try to call from Ajax:
public function changepriority($id=null)
{
$this->autoRender = false;
$message = $this->Messages->get($id);
$message->priority = ($message->priority === false) ? true : false;
if ($this->Messages->save($message)) {
echo json_encode($message);
}
}
And this is my ajax:
$(".email-star").click(function(){
var idmessage = this.id;
$.ajax({
headers : {
'X-CSRF-Token': $('[name="_csrfToken"]').val()
},
dataType: "json",
type: "POST",
evalScripts: true,
async:true,
url: '<?php echo Router::url(array('controller'=>'Messages','action'=>'changepriority'));?>' +'/'+idmessage,
success: function(data){
if(data['priority'] === false) {
$("#imp_" + idmessage).removeClass("fas").removeClass('full-star').addClass( "far" );
}
else {
$("#imp_" + idmessage).removeClass("far").addClass( "fas" ).addClass("full-star");
}
}
});
});
I have read the documentation about Cross Site Request Forgery, and I tried to turn off the Csrf for these action first with:
public function beforeFilter(Event $event)
{
$this->getEventManager()->off($this->Csrf);
}
and then with:
public function beforeFilter(Event $event)
{
$this->Security->setConfig('unlockedActions', ['index', 'changepriority']);
}
But nothing. The Xhr return always the CSRF token mismatch.
What can I do ?
Edit:
I change the action in this way:
public function changepriority($id=null)
{
$this->autoRender = false;
$message = $this->Messages->get($id);
$message->priority = ($message->priority === false) ? true : false;
if ($this->Messages->save($message)) {
$content = json_encode($message);
$this->response->getBody()->write($content);
$this->response = $this->response->withType('json');
return $this->response;
}
}
In that way the action works. Can it be like that?

First check your $('[name="_csrfToken"]').val() output.
If you didn't get any output, need to check csrfToken hidden field is exist or not. Just right click in your page and click View Page Source
If not exist, you don't follow proper way when you create Form. Basically, when forms are created with the Cake\View\Helper\FormHelper, a hidden field is added containing the CSRF token.
If everything is correct, add the following line inside your ajax call after header
beforeSend: function (xhr) {
xhr.setRequestHeader('X-CSRF-Token', $('[name="_csrfToken"]').val());
},
Ps. Disabling the CSRF is not recommended by cakePHP and most of the developer aware of this. Hope this help.

beforeSend: function (xhr) {
xhr.setRequestHeader('X-CSRF-Token', <?= json_encode($this->request->getAttribute('csrfToken')) ?>);
},

Related

How to handle response from Express response.write() in Angular $http

I'm trying to upload a csv file using ng-file-upoad. Here is my code snippet:
Upload.upload({
url: baseUrl + '/file-upload',
data: {
file: file
}
})
.then(function(res) {
console.log('success: ===> ', res);
}, function(err) {
console.log('erroir: ===> ', err);
}, function() {
console.log('progress: ', arguments);
});
And in node environment I'm parsing the file and inserting the data in database. I don't want to close the connection. That's why I used "response.write". Here is my code snippet:
var path = req.files.file.path,
currentIndex = 0;
fs.readFile(path, 'utf8', function(err, data) {
if(err) {
// handle error
} else {
// making array (dataArray) from data
dataArray.forEach(function(eachData){
newEntry = new app.db.models.SomeCollection(eachData);
newEntry.save(function(err, data) {
if (currentIndex === dataArray.length) {
res.end('DONE!');
} else {
currentIndex++;
res.write(JSON.stringify({
total: dataArray.length,
done: currentIndex
}));
}
});
})
}
});
My question is how I will get the data I'm passing in "res.write"? I don't want to use socket for only this purpose. Am I missing something?
As already explained here:
response.send(msg) is equal to response.write(msg);response.end();
Which means, send can only be called once, write can be called many times, but you must call end yourself.
You are probably not receiving the response because response.end() is missing.
Once you end() your response you should be able to access the response data in your angular controller in the Upload.upload promise that is returned.
It's not like close a connection as you said. This is not a socket-ish like implementation (such as ws or socket.io). Once a request is made it should have a response even if it is to provide error details about that request (i.e. status 401, 403, 404, etc).
in your angular component:
...
constructor(private incrementalService: IncrementalService) {}
incrementalTest() { //activate with a button or whatnot
this.incrementalService.increment().subscribe( (result:any) => {
if (result.partialText) {
console.log(partialText); //do whatever you need to do with your partial results here!
}
})
}
your angular service:
import { HttpClient, HttpHeaders } from '#angular/common/http';
public class IncrementalService {
constructor(private http: HttpClient) {}
increment(): Observable<ArrayBuffer> {
const options = {
reportProgress: true,
responseType: 'text',
observe: 'events'
}
return this.http.request('get', 'http://someURL', { ...this.addRawHeaderOptions(), ...options});
}
private addRawHeaderOptions() {
const authHeaders = new HttpHeaders({
'Content-Type': 'application/json',
//authorization, Cache-Control: 'no-cache, Pragma:'no-cache', et al. }
return { headers: authHeaders }
}
}
Finally, your back-end service (this is express, but should work similarly for raw node):
async function(request, response) {
const increments = [ 1,2,3,4 ];
response.set('Content-Type', 'text/html');
for (const value of increments) { //contains async call - not switch-outable for a forEach.
response.write(`increment - ${value} `);
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
await delay(1000)
}
response.status(200).end()
}
browser console output when run:
increment - 1
increment - 1 increment - 2
increment - 1 increment - 2 increment - 3
increment - 1 increment - 2 increment - 3 increment - 4
!!Sorry for any typos - i had to transcribe this from a locked-down machine.

Recaptcha in Angular JS

I am implementing recaptcha in Angular JS, I am using "https://github.com/VividCortex/angular-recaptcha" url as reference. I have referred the Usage section and followed the instructions of code but still not able to get recaptcha in registration page.
Steps I have followed -
1. Generated a public key.
2. Added
3. Added div for recaptcha
Added the anular-recaptcha.js in page - downloded form github code of above url.
Can anyone please let me know what I am missing in it? Can anyone can give me demo example link for recaptcha?
Thanks in advance.
example that woked for me
register.cshtml:
<div vc-recaptcha key="'domain-key'" on-success="setResponse(response)"></div>
app.js:
var app = angular.module('myApp', ['ngRoute','vcRecaptcha']);
controller.js:
var ctrl = function ($rootScope, $scope, $location, $routeParams, registrationService) {
$scope.reCaptchaResponse = "";
$scope.setResponse = function (response) {
$scope.reCaptchaResponse = response;
};
$scope.register = function () {
var registration = {
...
reCaptchaResponse: $scope.reCaptchaResponse
}
$http.post(serviceBase + 'Register', registration).then(function (results) {
//return result
});
}
}
Controller.cs:
[HttpPost]
public JsonResult Register(UserDTO registration)
{
string responseFromServer = "";
WebRequest request = WebRequest.Create("https://www.google.com/recaptcha/api/siteverify?secret=mysecret&response=" + registration.ReCaptchaResponse);
request.Method = "GET";
using (WebResponse response = request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(stream);
responseFromServer = reader.ReadToEnd();
}
}
if(responseFromServer != "")
{
bool isSuccess = false;
Dictionary<string, string> values = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseFromServer);
foreach (var item in values)
{
if (item.Key == "success" && item.Value == "True")
{
isSuccess = true;
break;
}
}
if(isSuccess)
{
//do something
}
else
{
//return reCaptcha error
}
}
return Json(result);
}
Sorry if you have already checked this...
They have a demo file here.
They also mention ..."Keep in mind that the captcha only works when used from a real domain and with a valid re-captcha key, so this file wont work if you just load it in your browser."
I followed their instructions and it worked okay for me.
Step1: Include captcha directive in form.html
<body ng-app="angularRecaptcha">
<div class="col-md-6 col-md-offset-3 signupform" ng-controller="recapCtrl as recap">
<form name="recap.signupForm" ng-submit="recap.signup()">
.....
..
<!--Recaptcha Widget--->
<div vc-recaptcha key="recap.publicKey"></div>
...
.....
</form>
</div>
<body>
Step2: Next is the App.js
The angular-recaptcha library provides us with the vcRecaptchaService that has a getResponse() method, which provides us with g-captcha-response string after the user has successfully solved the captcha.
angular.module(‘angularRecaptcha’,[‘vcRecaptcha’])
.controller('recapCtrl',['vcRecaptchaService','$http',function(vcRecaptchaService,$http){
var vm = this;
vm.publicKey = "----YOUR------SITE--------KEY---";
vm.signup = function(){
/* vcRecaptchaService.getResponse() gives you the g-captcha-response */
if(vcRecaptchaService.getResponse() === ""){ //if string is empty
alert("Please resolve the captcha and submit!")
}else {
var post_data = { //prepare payload for request
'name':vm.name,
'email':vm.email,
'password':vm.password,
'g-recaptcha-response':vcRecaptchaService.getResponse() //send g-captcah-response to our server
}
/* MAKE AJAX REQUEST to our server with g-captcha-string */
$http.post('http://sitename.com/api/signup',post_data).success(function(response){
if(response.error === 0){
alert("Successfully verified and signed up the user");
}else{
alert("User verification failed");
}
})
.error(function(error){
})
}
}
}])
Step 3: Handle the POST request at api endpoint using SLIM PHP framework
<?php
$app->post('/signup',function() use($app){
$req = $app->request()->getBody(); //get request pramans
$data = json_decode($req, true); //parse json string
//Should be some validations before you proceed
//Not in the scope of this answer.
$captcha = $data['g-recaptcha-response']; //Captcha response send by client
//Build post data to make request with fetch_file_contents
$postdata = http_build_query(
array(
'secret' => '-----YOUR SECRET KEY-------', //secret KEy provided by google
'response' => $captcha, // g-captcha-response string sent from client
'remoteip' => $_SERVER['REMOTE_ADDR']
)
);
//Build options for the post request
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
//Create a stream this is required to make post request with fetch_file_contents
$context = stream_context_create($opts);
/* Send request to Googles siteVerify API */
$response=file_get_contents("https://www.google.com/recaptcha/api/siteverify",false,$context);
$response = json_decode($response, true);
if($response["success"]===false) { //if user verification failed
/* return error scenario to client */
echo json_encode(array(
"error" => 7,
"message" => "Robots Not allowed (Captcha verification failed)",
"captchaResult" => $response["success"],
"captcahErrCodes" => $response["error-codes"] //error codes sent buy google's siteVerify API
));
} else {
//Should be some Datatbase insertion to sign up the user
//before you return the success response
//Not in the scope of this answer.
/* return success scenario to client */
echo json_encode(array(
"error" => 0,
"message" => "Successfully signed up!",
"email" => $data['email'],
"captchaResult" => $response["success"]
));
}
});
?>
When working with angularjs and google recaptcha, the library you have used is the best option.
But you have to take care of the following things for it to work.
Include the library as explained into your angular project.
Register your website and get the site keys.
Include the widget, use your Site Key.
Get the required g-captcha-response after the user solves the captcha.
Make an ajax request to your server with the G-captcha-reposnse.
On your backend verify the g-captcha-response using google's site verify API.
This link has a good explaination with a working example. Google recaptcha with angularJS

Uncaught TypeError: Cannot read property 'responseText' of undefined

buttons: [
{
text: "Add User",
id: "new-record-add-button",
handler: function() {
var form = this.up('form').getForm();
form.submit({
url: BasePath+'/main/admin/adduser',
method: 'POST',
waitTitle: 'Authenticating',
waitMsg: 'Please Wait',
success: function(form, action) {
win.close()
Ext.Msg.show({
title:'Success'
,msg:'User added successfully'
,modal:true
,icon:Ext.Msg.INFO
,buttons:Ext.Msg.OK
});
},
failure: function(form, action) {
console.log(action.response.responseText);
obj = Ext.JSON.decode(action.response.responseText);
console.log(obj);
Ext.Msg.alert('Error',obj.errors)
form.reset();
}
})
//this.up("window").close();
}
},
{
text: "Cancel",
handler: function() {
this.up("window").close();
}
}
]
I am getting the following error when I reach the failure function in my form:
Uncaught TypeError: Cannot read property 'responseText' of undefined
This is my php code:
public function adduserAction()
{
$response = new JsonModel();
//$error = array();
$errors="";
if(!ctype_alpha($_POST["first_name"])) {
$errors.="First Name cannot contain characters and numbers";
}
if(!ctype_alpha($_POST["last_name"])) {
$errors.="Last Name cannot contain characters and numbers";
}
if(!filter_var($_POST['email_address'], FILTER_VALIDATE_EMAIL)) {
$errors.="Email should be of the format john.doe#example.com";
}
if(empty($_POST["role"])) {
$errors.="Role cannot be empty";
}
if($errors!="") {
$response->setVariables(array("success"=>false, "errors"=>$errors));
}
else {
$response->setVariables(array("success"=>true, "errors"=>$errors));
}
return $response;
}
responseText is an ExtJs thing - it represents the actual text returned from the server (eg, using echo) before being decoded.
You should get it in asynchronous callbacks within the operation or request objects, unless there was a server exception of a sort or if you set success to false, that's why you don't get it in the failure handler.
To really understand what's going on with it I recommend you have a look at Connection.js.
if you do a form submit through ExtJs, then on success of the form submission it needs response.responseText to be set as {"sucess":"true"}. if you are submitting the form to some page you have to make sure that you will be returning this object from backend. Or else you have to override the existing onSuccess method.
The second way is something like this,
Ext.override(Ext.form.action.Submit,{
onSuccess: function(response) {
var form = this.form,
success = true,
result = response;
response.responseText = '{"success": true}';
form.afterAction(this, success);
}
});
Place this snippet in your application and see the magic. Cheers. :)

Ext Js ajax request not working for failure function

here is my function sending ajax request for checking email duplication while getting response from my php file , not falling on failure function it returns only in success function , for this i have checked result.responseText == to "false" and with else condition now in this condition it shows the error popup message too but donot abort the request continuoues its execution and saves the data
function email_check(userType) {
//alert(userType);
var extmail= Ext.Ajax.request({
url: '<?= Extjs_renderer::ajaxurl();; ?>ajax/emailcheck',
success: function ( result, request ) {
if(result.responseText=='false'){
//Ext.Ajax.abort(extmail); tried
Ext.MessageBox.alert('Error', "email already exist");
// return false;
//Ext.getCmp('email').setValue(''); works
}else {
return true;
}
},
failure: function(response, options) {
Ext.MessageBox.alert('Error', "email already exist fail");
},
params: {merc_mem_tab:userType }
});
}
here is my ajax.php code
function emailcheck(){
$get_email=$this->db->query("select * from customers where email='".$_REQUEST['merc_mem_tab']."'");
if($get_email->num_rows==0){
echo "true";
return true;
}else{
echo "false";
// echo "{success: true}";
return false;
}
}
while on my panel handler i am also trying to check the response but could not succeeded
if('<?= $this->controller->name; ?>'=="customers"){
//alert(Ext.getCmp('email'))
if(email_check(Ext.getCmp('email').getValue()) == false){
return false;
}
}
You can't return from an ajax request, It is asyncron, and this bit of code if(email_check(Ext.getCmp('email').getValue()) == false) won't wait for the answer.
Also the failure is as Imad said, just for http failures not for false responses. your code to check for response false was correct but i suggest you call a saving method on the success function.Like:
function email_check(userType) {
//alert(userType);
var extmail= Ext.Ajax.request({
url: '<?= Extjs_renderer::ajaxurl();; ?>ajax/emailcheck',
scope: this,
success: function ( result, request ) {
if(result.responseText=='false'){
Ext.MessageBox.alert('Error', "email already exist");
//do nothing else
}else {
this.saveData();
}
},
failure: function(response, options) {
Ext.MessageBox.alert('Error', "Communication failed");
},
params: {merc_mem_tab:userType }
});
}
The choice of success or failure callback is based on the HTTP response code. So, if you want to reach the failure function, you'll have to do some :
function emailcheck(){
$get_email=$this->db->query("select * from customers where email='".$_REQUEST['merc_mem_tab']."'");
if($get_email->num_rows==0){
echo "true";
return true;
}else{
throw new Exception("Error : Email Already Exists !");
}
}
It should provoke an error 500 (exception unhandled) and ExtJS will identify it as a failure response.

persisting filters in grid panel

I would like to persist filters applied on gridpanel on page refresh. Can you please guide me in doing this.
Thanks.
Here is the code which send the filter data to webservice
Ext.extend(Ext.ux.AspWebServiceProxy, Ext.data.DataProxy,
{
load: function(params, reader, callback, scope, arg) {
var userContext = {
callback: callback,
reader: reader,
arg: arg,
scope: scope
};
var proxyWrapper = this;
//debugger;
//Handles the response we get back from the web service call
var webServiceCallback = function(response, context, methodName) {
proxyWrapper.loadResponse(response, userContext, methodName);
}
var serviceParams = [];
var filters = {};
//Convert the params into an array of values so that they can be used in the call (note assumes that the properties on the object are in the correct order)
for (var property in params) {
if (property.indexOf("filter[") == 0) {
filters[property] = params[property];
}
else {
serviceParams.push(params[property]);
}
//console.log("Property: ", property, "Value: ", params[property]);
}
serviceParams.push(filters);
//Add the webservice callback handlers
serviceParams.push(webServiceCallback);
serviceParams.push(this.handleErrorResponse);
//Make the actual ASP.Net web service call
this.webServiceProxyMethod.apply(this.webServiceProxy, serviceParams);
},
handleErrorResponse: function(response, userContext, methodName) {
window.location.reload();
// Ext.MessageBox.show({
// title: 'Error',
// msg: response.get_message(),
// buttons: Ext.MessageBox.OK,
// icon: Ext.MessageBox.ERROR
// });
//alert("Error while calling method: " + methodName + "n" + response.get_message());
},
loadResponse: function(response, userContext, methodName) {
var result = userContext.reader.readRecords(response);
userContext.callback.call(userContext.scope, result, userContext.arg, true);
}
});
Turn on the Ext JS state manager globally (where you set Ext.BLANK_IMAGE_URL).
Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
User changes to some components will now be stored in a cookie, which will persist across requests. If you need to store additional custom data, you can do that using Ext.state.Manager.set and Ext.state.Manager.get. State is configurable on individual components.
Saki has a good example.
To persist filters on grid you can use cookies, here you can find some help:
proxy: new Ext.data.HttpProxy({
url: (local ? url.local : url.remote),
method: 'GET',
listeners:{
beforeload : function(dataproxy,param) {
if(param.searchConditions != undefined && param.searchConditions != '[]') {
Ext.util.Cookies.set('SearchConditions',param.searchConditions);
}
}
}
})
In above sample you can find that we are setting "searchConditions" JSONArray in cookies.Now let us see how to get back that "searchCondition" whenever you load you Grid.
store.load({
params:{
start:0,
limit: 50,
searchConditions:JSON.parse(Ext.util.Cookies.get('SearchConditions'));
}
});
Here simply you just need to pass your "searchCondition" parameter value as value stored in Cookie. Hope above example is useful.Please comment for any help.

Resources