I have included Zend_Form_Element_Hash into a form multiplecheckbox form. I have jQuery set to fire off an AJAX request when a checkbox is clicked, I pass the token with this AJAX request. The first AJAX request works great, but the subsequent ones fail.
I suspect it may be once the token has been validated it is then removed from the session (hop = 1).
What would be your plan of attack for securing a form with Zend Framework Hash yet using AJAX to complete some of these requests?
I finally abandoned using Zend_Form_Element_Hash and just created a token manually, registered it with Zend_Session and then checked it upon submission.
form.php
$myNamespace = new Zend_Session_Namespace('authtoken');
$myNamespace->setExpirationSeconds(900);
$myNamespace->authtoken = $hash = md5(uniqid(rand(),1));
$auth = new Zend_Form_Element_Hidden('authtoken');
$auth->setValue($hash)
->setRequired('true')
->removeDecorator('HtmlTag')
->removeDecorator('Label');
controller.php
$mysession = new Zend_Session_Namespace('authtoken');
$hash = $mysession->authtoken;
if($hash == $data['authtoken']){
print "success";
} else {
print "you fail";
}
This seems to work and still keeps things relatively sane and secure. I'd still rather use the Hash element, but I can't seem to make it work with AJAX.
Thanks all.
That's how to handled hash field in ajax form :
class AuthController extends Zend_Controller_Action
{
public function init()
{
$contextSwitch = $this->_helper->getHelper('contextSwitch');
$contextSwitch->addActionContext('index', 'json')
->initContext();
}
public function loginAction()
{
$form = new Application_Form_Login();
$request = $this->getRequest();
if ($request->isPost()) {
if ($form->isValid($request->getPost())) {
// some code ..
} else {
// some code ..
// Regenerate the hash and assign to the view
$reservationForm->hash->initCsrfToken();
$this->view->hash = $reservationForm->hash->getValue();
}
}
$this->view->form = $form;
}
}
And then in your view script ..
<? $this->dojo()->enable()
->requireModule('dojox.json.query')
->onLoadCaptureStart() ?>
function() {
var form = dojo.byId("login_form")
dojo.connect(form, "onsubmit", function(event) {
dojo.stopEvent(event);
var xhrArgs = {
form: this,
handleAs: "json",
load: function(data) {
// assign the new hash to the field
dojo.byId("hash").value = dojox.json.query("$.hash", data);
// some code ..
},
error: function(error) {
// some code ..
}
}
var deferred = dojo.xhrPost(xhrArgs);
});
}
<? $this->dojo()->onLoadCaptureEnd() ?>
Hope it's not too late :D
There is a solution:
Create, besides the form that will contain the data, a form without elements. From the controller you instantiate the two forms. Also in the controller, you add the element hash to the empty form. Both forms should be sent to the vision. Then, in the condition "if ($ request-> isXmlHttpRequest ())" in the controller you render the empty form. Then, you take the hash value with the method "getValue ()". This value must be sent in response by Ajax and then use JavaScript to replace the hash value that is already obsolete. The option to create an empty form for the hash is to avoid problems with other elements such as captcha that would have its id generated again if the form were rendered, and would also need to have the new information replaced. The validation will be done separately because there are two distinct forms. Later you can reuse the hash (empty) form whenever you want. The following are examples of the code.
//In the controller, after instantiating the empty form you add the Hash element to it:
$hash = new Zend_Form_Element_Hash('no_csrf_foo');
$hash_form->addElement('hash', 'no_csrf_foo', array('salt' => 'unique'));
//...
//Also in the controller, within the condition "if ($request->isXmlHttpRequest())" you render the form (this will renew the session for the next attempt to send the form) and get the new id value:
$hash_form->render($this->view);
$hash_value['hash'] = $hash_form->getElement('no_csrf_foo')->getValue();//The value must be added to the ajax response in JSON, for example. One can use the methods Zend_Json::decode($response) and Zend_Json::encode($array) for conversions between PHP array and JSON.
//---------------------------------------
//In JavaScript, the Ajax response function:
document.getElementById("no_csrf_foo").value = data.hash;//Retrieves the hash value from the Json response and set it to the hash input.
Leo
Form hashes are great in principle and a bit of a nightmare in practice. I think the best way to handle this is to return the new hash with the response when you make a request, and update the form markup or store in memory for your javascript as appropriate.
The new hash may be available from the form object, or you can read it from the session.
You hinted at the right answer in your question: increase the hop count.
There was specific mention of this in the ZF manual online, but they updated their manuals and now i can't find it (grin)- otherwise i would have posted the link for you.
If you want to use form validator in ajax side use following code :
Myform.php
class Application_Form_Myform extends Zend_Form
{
# init function & ...
public function generateform($nohash = false)
{
# Some elements
if(!$nohash)
{
$temp_csrf = new Zend_Session_Namespace('temp_csrf');
$my_hash = new Zend_Form_Element_Hash ( 'my_hash' );
$this->addElement ( $my_hash , 'my_hash');
$temp_csrf->hash = $my_hash->getHash();
}
# Some other elements
}
}
AjaxController.php
class AjaxController extends Zend_Controller_Action
{
// init ...
public function validateAction()
{
# ...
$temp_csrf = new Zend_Session_Namespace('temp_csrf');
if($temp_csrf->hash == $params['received_hash_from_client'])
{
$Myform = new Application_Form_Myform();
$Myform->generateform(true);
if($AF_Bill->isValid($params))
{
# Form data is valid
}else{
# Form invalid
}
}else{
# Received hash from client is not valid
}
# ...
}
}
Related
I have the following code on my site which sending data to Ajax and works normal. It is about add to cart.
public function adding()
{
$id_lap=$this->input->post('id_lap');
$num_items=$this->input->post('num_items');
$price=$this->input->post('price');
if($id_lap !='')
{
$this->session->set_userdata('num_items',$num_items);
$this->session->set_userdata('price_new',$price);
$response=array(
'status'=>1,
'num_items'=>$num_items,
'price_new'=>$price
);
} else {
$response=array(
'status'=>0
);
}
echo json_encode($response);
}
When I want to send data to model also where I want to write data into database along with IP adress which I will collect with model. When I add new row anywhere my Ajax crashed. Part of code which I want to add is:
$this->users_mod->AddSesija($id_lap,$price);
And Ajax code which I am using is :
function Addtocart(id,prices)
{
var id_lap=id;
var ajaxURL= BASE_URL +'home/adding';
var num_items = parseInt($('#num_items').html()) + 1;
var price = parseInt($('#price_b').html());
var price_new = price + prices;
/*console.log(id_lap +' '+num_items+' '+price_new);
$('#num_items').html(num_items);
$('#price_b').html(price_new); */
$.ajax({
url:ajaxURL,
type:'POST',
data:{'id_lap':id_lap,'num_items':num_items,'price':price_new},
success:function(response)
{
var parsed = JSON.parse(response);
if(parsed.status ==1)
{
$('#num_items').html(parsed.num_items);
$('#price_b').html(parsed.price_new);
}
}
}
)
}
I am agree with Vladimir Glisovic in concept. we know that for adding or inserting group of data we should bind them into an array.
Sample:
$data=array( 'ip_adress'=>$ip_adress, 'id_lap'=>$id_lap, 'cena'=>$price );
N.b: get IP address from PHP code keep in $ip_address.
Then pass the data to model with the CI syntax.
Model calling in Controller:
$this->users_mod->AddSesija($data);
Now write the inserting code in the model file as
public function AddSesija($data){
insertion code here.....
}
Hope it will help you.
Thanks!
Amadercode
As soon as a page loads, it renders a lot of data and shows on the view which sort of slows down the performance. I want to restrict this and load the data only when a filter is applied .
I need a way in which a session variable can store the value on the 1st login and no data should be loaded in that 1st session i.e. when any user loads it for the very first time using his login. something like the below in the controller class:
if(session.dtstartDate && session.dtstartDate != '')
{
SimpleDateFormat nsdf = new SimpleDateFormat('yyyy-MM-dd')
Date startDateValue = nsdf.parse(session.dtstartDate.substring(0, session.dtstartDate.lastIndexOf("T")))
eq("startDate", startDateValue)//if any filter is applied
}
else{
if this is the 1st session the startdate should be null --> need a piece of code to be replaced here
}
I am unsure maybe I still have not got what you have tried to ask properly, I thought I should try to answer your question to how I understood your problem to be.
If we take this basic filter and add some stuff to it we may be able to get to what you wish to do using a better method ? I am unsure what startDate is actually representing but if we base it on if a user has hit a controller for the first time or not the answer would be something like this, you could replace the logic to startDate if it has other significance:
so adding some hashmap arrayset to your filter that gets called before the action is called when user clicks the controller/action:
in your conf/MyFiters.groovy
class MyFilters {
static final Set<HashMap<String[],String[]>> userControl =
([:] as Set).asSynchronized()
//where controller is controllerName and action is actionName
def filters = {
MyLogger() {
before = {
if (verifyClientMaster(session.id as String ,controllerName)==false) {
clientMaster.add(session.id as String:controllerName)
// now here you have a new user so set
// some session value for gsp or load something
//according
}else{
// user has hit it before do something else or set something else
}
}
}
}
}
Boolean verifyClientMaster(String sessionId,String controller) {
// iterate
boolean found = false
userControl.each { k,v -> if (k == sessionId && v == controller) {
found = true
}
}
}
something like this and you know if the user has hit the controller or not.. remember the session is per user. so a new user has a new session entity.
Hope it is of help and not off track..
E2A
Thinking about it you do go down this route then you would need to keep track of when session expires and to remove the user from clientMaster.. take a look at this project if you did go down this route.. personally I would even do it simpler than this... on a rethink...
class MyFilters {
def filters = {
MyLogger() {
before = {
if (!sessions."${controllerName}") {
sessions."${controllerName}"="${controllerName}"
// now here you have a new user so set
// some session value for gsp or load something
//according
}else{
// user has hit it before do something else or set something else
}
}
}
}
}
and even simple than any of this would be to use the intelligence built into a gsp if what you load can be based on it... (not tested any of it ha)
<g:if test="${!session."${controllerName}"}">
<g:set var="${controllerName}" value="${controllerName}" scope="session" />
<g:render template="firstTimeHitter"/>
</g:if>
<g:else>
<g:render template="secondTimeHitter"/>
</g:else>
or just your controller that checks and sets that and either renders something different or sets something gsp picks up on..
def myController {
def doSomething() {
boolean firstTime = false
if (!session."${controllerName}") {
// first time either render or set firsTime
firstTime = true
session."${controllerName}" = controllerName // or startDate
// render view: 'firstTime, model: [firstTime:firstTime, params:params]
} else{
// render view: 'firstTime, model: [firstTime:firstTime, params:params]
}
// if no render above:
render view: 'doSomething, model: [firstTime:firstTime, params:params]
// now in doSomething gsp you look for firstTime:
}
do someThing:
<g:if test="${firstTime.toString().equals('true')}">
<g:render template="firstTimeHitter"/>
</g:if>
<g:else>
<g:render template="secondTimeHitter"/>
</g:else>
The possibilities are endless, the differences being with a filter its a one fits all, i.e. it is checking every controller as it is hit by each user. In controller and gsp solution you have to declare it where needed. You could have an abstract controller that other controllers extend to repeat that check as a higher class that gets called to verify, regardless their all a lot more repetitive than a simple one off filter...
Final Edit to give other other alternatives would be:
final Set<Session> jsessions = ([] as Set).asSynchronized()
jsessions.add('controller1')
jsessions.add('controller2')
jsessions.add('controller3')
jsessions.add(controllerName)
println "=== ${jsessions}"
if (jsessions.contains(controllerName)) {
println "--- We have ${controllerName} defined in our session set.... jsessions"
}
ArrayList jsessions2 = []
jsessions2.add(controllerName)
session.jsessions2 = jsessions2
//repeat this on every call
ArrayList jsessionsret = session.jsessions2
jsessionsret.add('controller1')
jsessionsret.add('controller2')
jsessionsret.add('controller3')
session.jsessions2 = jsessionsret
if (jsessions2.contains(controllerName)) {
println "--- We have ${controllerName} defined in our session set.... jsessionsret"
}
println "222 --- ${jsessions2}"
This segment above are two different implementations of using first a session set that is global and could be used if you do not care if the controller is hit by usera userb etc so if usera hits it userb would also be considered as hitting it.. This is jsessions.
The bottom jsessions2 is an attempt to turn a single session key into an ArrayList. So rather than storing lots of single object i.e. session."${controllerName}" per call of a controller per user session. You could have 1 single session key per user that you append each controller they hit to.. and you then check to see if they have that controller
I read this article:
http://www.jeremykendall.net/2009/01/19/dynamically-adding-elements-to-zend-form/
That was very interesting and it works fine.
I need to do the same but with a SubForm. I mean that when a user presses a button, I call, via ajax, an action that adds, attaches and displays a subform to my existing form.
For example:
I have a form where a user must fill in the name and surname of his children, so there is a button "Add Child". When the user presses that button a SubForm should be added to my existing form and displayed. On submit it will validate exactly like the example in that article. The only difference is that in there he just adds a single field. I need to add a SubForm, but in exactly the same way.
I tried the following in my action ( called by Ajax ):
public function clonerecursivegroupAction()
{
$this->_helper->layout->disableLayout();
$ajaxContext = $this->_helper->getHelper('AjaxContext');
$ajaxContext->addActionContext('clonerecursivegroup', 'html')->initContext();
$id = $this->_getParam('id', null);
$subform1 = new Zend_Form_SubForm();
$Element1 = $subform1->createElement('text', 'text1');
$Element1->setLabel('text1')->setRequired(true);
$Element2 = $subform1->createElement('text', 'text2');
$Element2->setLabel('text2')->setRequired(false);
$subform1->addElement($Element1);
$subform1->addElement($Element2);
$this->view->field = $subform1->__toString();
}
This almost works.
The view of this action returns the html code of the SubForm, so on success of my ajax call I just display it.
The problem is that on submit it validates the form but it has lost the new subform just added. That does not happen in the article with just one element. I think I just need to add the SubForm to the existing Form, but how?
Add a prefix of the subform to the subform elements. I used the prefix "child" to represent the subforms. Each subform will be created as child1, child2 and so on.
public function clonerecursivegroupAction()
{
//.. Other code
$subform = new Zend_Form_SubForm();
$subform->setIsArray(true);
$subform->setName("child$id");
$Element1 = $subform->createElement('text', "newfield$id");
$Element1->setLabel("newfield$id")
->setRequired(true);
$subform->addElement($Element1);
$Element1 = $subform->createElement('text', "nextfield$id");
$Element1->setLabel("nextfield$id")
->setRequired(true);
$subform->addElement($Element1);
$this->view->field = $subform;
// Rest of your statements
}
Then, in the preValidation function, filter the subforms using the subform prefix, instead of the field name:
public function preValidation(array $data) {
// array_filter callback
function findForms($field) {
// return field names that include 'child'
if (strpos($field, 'child') !== false) {
return $field;
}
}
$subForms = array_filter(array_keys($data), 'findForms'); //filter the subform elements
$children = array();
foreach ($subForms as $subform) {
if (is_array($data[$subform])) {
$children[$subform] = $data[$subform];
}
}
//Iterate the children
foreach ($children as $key => $fields) { //$key = subformname, $field=array containing fiend names and values
// strip the id number off of the field name and use it to set new order
$order = ltrim($key, 'child') + 2;
$this->addNewForm($key, $fields, $order);
}
}
Add New Form function creates each of the sub forms and attaches to the main form:
public function addNewForm($form, $elements, $order) {
$subform = new Zend_Form_SubForm();
$subform->setIsArray(true);
foreach ($elements as $key => $el) {
$Element1 = $subform->createElement('text', $key);
$Element1->setLabel($form.$key)
->setValue($el)
->setRequired(true);
$subform->addElement($Element1);
}
$this->addSubForm($subform, $form, $order);
}
[EDIT] Using setIsArray for a subform creates each element of the subform as an array element. It simplifies the preValidate function. Edited the code to utilize this feature.
See the complete code in pastebin
Here is another solution using belongsTo, providing array notation to the sub form elements : http://www.stephenrhoades.com/?p=364
I am trying to access an object with form data sent to my controller. However, when I try to access objects I get values of null or 0. I used two methods, the first by serializing and the second by storing names and values in one object. (the code below sends/posts serialized)
Here is my JS...
$("#createUser").click(function() {
//store input values
var inputs = $('#newUserForm :input');
var input = $('#newUserForm :input').serializeArray();
console.log(input);
//if I want just the values in one object
var values = {};
$(inputs).each(function() {
values[this.name] = $(this).val();
});
console.log(values);
if(LiveValidation.massValidate( validObj )){
$.post('./adminPanel/createUser', function(input){
alert('Load was performed.');
//test confirmation box
$("#msgbox").html("Grrrrreat");
//drop down confirmation
$("#msgbox").slideDown();
});
} else {
//test fail box
$("#failbox").html("Fail");
$("#failbox").slideDown();
}
});
In the controller side I try to access data the following way...
$this->input->post("firstName")
where firstName is the name of the field.
Below is an image of the objects passed.
Top being serialized array and the bottom a single object with all the names and values of form...
If you're using jQuery, you can use jQuery's built in serialize/query string functions to get the data from a form: http://api.jquery.com/serialize/
In your case:
var data = $('#newUserForm').serialize(); // is a string like "firstName=jon"
I'm trying to send some data via dojo.xhrPost to an Zend Controller Action. I can see the data being sent in Firebug console. However, when inspecting the post data, the array is empty.
I'm not sure if it is possible to send an arbitrary string of data via dojo.xhrPost without using a form. This is probably a very n00b mistake. In any case, I'll post my code here and see what you all think.
In my layout script I have:
<?php
$sizeurl = $this->baseUrl() . '/account/uisize';
?>
function resizeText(multiplier)
{
if (document.body.style.fontSize == "")
{
document.body.style.fontSize = "1.0em";
}
document.body.style.fontSize = parseFloat(document.body.style.fontSize) + (multiplier * 0.1) + "em";
var size = document.body.style.fontSize;
var xhrArgs = {
url: "<?= $sizeurl; ?>",
postData: size,
handleAs: "text"
}
dojo.xhrPost(xhrArgs);
}
Then my action is:
public function uisizeAction()
{
$this->_helper->viewRenderer->setNoRender();
$this->_helper->layout->disableLayout();
print_r($_POST);
$request = $this->getRequest();
if ($request->isXmlHttpRequest())
{
$postdata = $request->getPost();
print_r($postdata);
if ($postdata)
{
$user = new Application_Model_DbTable_User();
$user->updateSize($postdata);
}
}
}
I'm pretty sure that post data from a form is an array with the form elements' names as the keys. When looking at the dojo.xhrPost examples on the dojo campus web site (http://docs.dojocampus.org/dojo/xhrPost second one to be precise), it looks as if I can just send a string of data. How do I access this data from a Zend Controller Action?
I'm using ZF 1.10 and Dojo 1.4.2
Thanks for your help!
PS
I'd try to ask on one of the related questions, but I cannot seem to comment.
After reading about http methods here:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
I figured that I need to encode the data sent in a way that will be converted to an array by PHP. So here is the new bit of javascript:
function resizeText(multiplier)
{
if (document.body.style.fontSize == "")
{
document.body.style.fontSize = "1.0em";
}
document.body.style.fontSize = parseFloat(document.body.style.fontSize) + (multiplier * 0.1) + "em";
var rawdata = "uisize="+document.body.style.fontSize;
var xhrArgs = {
url: "<?= $sizeurl; ?>",
postData: rawdata,
handleAs: "text"
}
//Call the asynchronous xhrPost
dojo.xhrPost(xhrArgs);
}
The difference is I am now specifying a key pair and sending that. When using AJAX that could make forms overkill. So now my UI is resized and the size is stored with the user's profile. So the next page they request will use the size they set. Cool.