Cake PHP pass parameter with form to controller - forms

I don´t know how to securely pass a parameter via form with Cake.
The method I use now is as follows:
$this->Form->create('Post', array('label' => '', 'action' => '', 'url' => 'inseratAngenommen/'.$postId));
In the controller there stands:
function inseratAngenommen($id = null, $bs = null){
//stuff
}
The poblem is that the user can modify the output number of $postId in the browser:
action="/cakephp/posts/inseratAngenommen/149"
For that case I want to pass the parameter invisible in the HTML. Is that possible?
I thought of a method like the Form->PostLink provides. I couldn´t find anything.
Thanks in advance.

It is not possible to send an parameter securely over a website as the data is sent by the user.
Use the validation methods of cakephp to make sure the data is correct.

1] method one: add obscurity: hide the $id into a posted field by:
$this->Form->hidden('id');
$this->Form-field('id'); // even this one will do as cake hides ids by default
2] method two: keep the id on the server for instance in a session
$this->Session->write('current-edited-post-id', $id); // upon form display
$id = $this->Session->read('current-edited-post-id'); // upon form submission
but be warned thou, that method 2 doesn't behave well, if the user opens multiple tabs and operates one session from both of them :(

Related

Trim function before validating form input in Codeigniter 4

With Codeigniter 3 it was possible to use "trim" as a validation rule.
It seems it is no more possible with Codeigniter 4.
Then how can I trim input values before validating, in case the user left whitespaces at the beginning or the end of the input?
$validation->setRule('username', 'Username', 'trim|required|min_length[3]');
I thought using a custom rule but these functions can only return true or false. They can't modify the input. The other solution is using the php trim function but I can't see where to use it.
Thanks for your help!
I'm guessing you're validating the post request directly. For what you need I would validate your modified array instead of the post request directly.
One of the great things in codeigniter 4 validation is that your can actually validate anything. Unlike codeigniter 3 where you could only use it to validate the $_POST data.
Let's say you have two fields, username and password and want to trim the username.
In you controller that would get the post date you would do the following.
$validation = \Config\Services::validation();
$validation->setRules([
'username' => 'required',
'password' => 'required|min_length[10]'
]);
$data = $this->request->getPost();
$data['username'] = trim($data['username']);
if (!$validation->run($data)) {
// handle validation errors
}
If you're doing the validation in the model, I'm not sure if the validation is run before the callbacks but its worth a try. So You would define a function in your beforeInsert callback and handle the trim there.
More about callbacks here:
https://codeigniter.com/user_guide/models/model.html#specifying-callbacks-to-run
If that does not work you can even remove the username from your validation rules in your model and then in a beforeFind and beforeUpdate function validate the username yourself and trim it.
I had the exact same question. For me, it makes sense to trim most POST variables before any validation. Even some of the common validation rules are best executed with already-trimmed values.
As #micthi stated, CodeIgniter 3 offered an easy way to trim just before validation. CodeIgniter 4 makes it much less straightforward. A custom validation rule can't modify data for us, and the model event methods such as beforeInsert and beforeUpdate also don't help us in running callbacks as they all execute after validation.
Below is a CodeIgniter 4 solution that works to allow trimming of POST variables before any validation. In brief, a filter is created and then configured to run before any controller code is executed for any POST method. It loops thru the $request object to trim the POST variables and then allows the newly-trimmed version if the $request to proceed to the controller.
Create: app/Filters/TrimFilter.php
namespace App\Filters;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
class TrimFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null) {
$trimmed_post = [];
foreach($request->getPost() as $var => $val) {
$trimmed_post[$var] = trim($val);
}
$request->setGlobal('post', $trimmed_post);
}
}
Modify: app/Config/Filters.php
// Add to the use statements
use App\Filters\TrimFilter;
// Add to the $aliases array
public $aliases = [
'trim' => TrimFilter::class
];
// Add to the $methods array
public $methods = [
'post' => ['trim']
];
ANOTHER OPTION: Instead of using the filter approach, you could instead perform the trimming loop within the BaseController.php file. In this case, remember to use $this-> to reference the request while within the BaseController.

do i need to validate select/option in drupal 7 form

I am creating my first drupal form and i am wondering if its needed to validate the select options? here is the form element
$form['page1']['color']=array(
'#type'=>'select',
'#title'=>t('Select Transmission'),
'#empty_value' => '',
'#options' => $color_options,
'#required'=>TRUE,
'#default_value' => !empty($form_state['values']['color']) ? $form_state['values']['color'] : '',
);
so since drupal have the hidden fields for security can i trust that this form is always sent unaltered from my website?
thanks
Michael
You don't need to validate the select options. Drupal will take care of it for you. If a user tries to alter the value of a option (that is not one of a key or your $color_options array) with Firebug (or whatever), he will get the message "An illegal choice has been detected. Please contact the site administrator." from Drupal.
Furthermore, you don't need to set a value from "$form_state" for the "#default_value" key. Just put one of the key of the "$color_options" for instance or don't use the key at all if you don't need a default value.

redirect forms with url values in zend

I have a page of the format www.test.com/policy-info/
which has a form that takes you to another page www.test.com/payment-info.
Now I am redirecting the page from www.test.com/payment-info to www.test.com/policy-info based on the some values obtained from the form. How do I redirect with certain values in the url.
the filename is policy-info.phtml.
Is this the only way to redirect?
$this->redirect(www.test.com/policy-info/index.phhtml&count=2);
You can use this:
$this->_helper->redirector('action', 'controller', 'module', array('param1'=>'value1', 'param2'=>'value2'));
in your case:
$this->_helper->redirector('index', 'policy-info', 'default',array('param1'=>'value1', 'param2'=>'value2'));
I always use $this->_redirect(...) and that works fine:
$this->_redirect('/module/controller/action/param1/value1/param2/value2');
This may be a case where _forward() may be the best choice.
_forward($action, $controller = null, $module = null, array $params = null): perform another action. If called in preDispatch(), the
currently requested action will be skipped in favor of the new one.
Otherwise, after the current action is processed, the action requested
in _forward() will be executed.
also It looks like you may be using named/defined routes, if that is true gotoRoute maybe useful as well Redirector Helper:
$this->_helper->getHelper('Redirector')->gotoRoute(array('param'=>'value'), 'routeName');

Unit Testing (PHPUnit): how to login?

I'm writing tests for my current project, made with Zend Framework.
Everything's fine, but I have a problem testing the logged users actions/controllers: I need to be logged in to be able to perform the action/controller.
How can I be logged in PHPUnit?
As you are saying you want to test actions/controllers, I suppose you are not writting unit-tests, but functional/integration tests -- ie, working with Zend_Test and testing via the MVC.
Here is a test-function I used in a project, where I'm testing if logging in is OK :
public function testLoggingInShouldBeOk()
{
$this->dispatch('/login/login');
$csrf = $this->_getLoginFormCSRF();
$this->resetResponse();
$this->request->setPost(array(
'login' => 'LOGIN',
'password' => 'PASSWORD',
'csrfLogin' => $csrf,
'ok' => 'Login',
));
$this->request->setMethod('POST');
$this->dispatch('/login/login');
$this->assertRedirectTo('/');
$this->assertTrue(Zend_Auth::getInstance()->hasIdentity());
}
Simply : I'm loading the login form, extracting the CSRF token, populating the form, and posting it.
Then, I can test if I'm connected.
With that, you can probably extract the logging-in part, to call it before each one of your tests that require a valid user to be logged-in.
There is another way. On my User entity I have a login() method that puts the user's id into the session and a static variable. What I just do in the test setUp() is call $user->login() and it works. In testing environment sessions are not used (setting Zend_Session::$isUnitTested = true has this effect) and tests rely on the static variable. Just remember to clear the static variable (logout() the user) on tearDown().
I think this article could help you:
http://perevodik.net/en/posts/7/
It describes how to create a fake identity you can use to set the environment to a state equivalent to a user being logged in.
In much the same way Pascal is using this function:
$this->_getLoginFormCSRF();
I have created a generic function that returns the value by loading the form using the form element manager:
public function _getCSRFHashValueFromForm($formAlias, $csrfName) {
$form = $this->servicemanager->get('FormElementManager')->get($formAlias);
return $form->get($csrfName)->getValue(); }
This of course assumes that the CSRF is bound to the form and not within any fieldset etc.

How do I use and debug WWW::Mechanize?

I am very new to Perl and i am learning on the fly while i try to automate some projects for work. So far its has been a lot of fun.
I am working on generating a report for a customer. I can get this report from a web page i can access.
First i will need to fill a form with my user name, password and choose a server from a drop down list, and log in.
Second i need to click a link for the report section.
Third a need to fill a form to create the report.
Here is what i wrote so far:
my $mech = WWW::Mechanize->new();
my $url = 'http://X.X.X.X/Console/login/login.aspx';
$mech->get( $url );
$mech->submit_form(
form_number => 1,
fields =>{
'ctl00$ctl00$cphVeriCentre$cphLogin$txtUser' => 'someone',
'ctl00$ctl00$cphVeriCentre$cphLogin$txtPW' => '12345',
'ctl00$ctl00$cphVeriCentre$cphLogin$ddlServers' => 'Live',
button => 'Sign-In'
},
);
die unless ($mech->success);
$mech->dump_forms();
I dont understand why, but, after this i look at the what dump outputs and i see the code for the first login page, while i belive i should have reached the next page after my successful login.
Could there be something with a cookie that can effect me and the login attempt?
Anythings else i am doing wrong?
Appreciate you help,
Yaniv
This is several months after the fact, but I resolved the same issue based on a similar questions I asked. See Is it possible to automate postback from the client side? for more info.
I used Python's Mechanize instead or Perl, but the same principle applies.
Summarizing my earlier response:
ASP.NET pages need a hidden parameter called __EVENTTARGET in the form, which won't exist when you use mechanize normally.
When visited by a normal user, there is a __doPostBack('foo') function on these pages that gives the relevant value to __EVENTTARGET via a javascript onclick event on each of the links, but since mechanize doesn't use javascript you'll need to set these values yourself.
The python solution is below, but it shouldn't be too tough to adapt it to perl.
def add_event_target(form, target):
#Creates a new __EVENTTARGET control and adds the value specified
#.NET doesn't generate this in mechanize for some reason -- suspect maybe is
#normally generated by javascript or some useragent thing?
form.new_control('hidden','__EVENTTARGET',attrs = dict(name='__EVENTTARGET'))
form.set_all_readonly(False)
form["__EVENTTARGET"] = target
You can only mechanize stuff that you know. Before you write any more code, I suggest you use a tool like Firebug and inspect what is happening in your browser when you do this manually.
Of course there might be cookies that are used. Or maybe your forgot a hidden form parameter? Only you can tell.
EDIT:
WWW::Mechanize should take care of cookies without any further intervention.
You should always check whether the methods you called were successful. Does the first get() work?
It might be useful to take a look at the server logs to see what is actually requested and what HTTP status code is sent as a response.
If you are on Windows, use Fiddler to see what data is being sent when you perform this process manually, and then use Fiddler to compare it to the data captured when performed by your script.
In my experience, a web debugging proxy like Fiddler is more useful than Firebug when inspecting form posts.
I have found it very helpful to use Wireshark utility when writing web automation with WWW::Mechanize. It will help you in few ways:
Enable you realize whether your HTTP request was successful or not.
See the reason of failure on HTTP level.
Trace the exact data which you pass to the server and see what you receive back.
Just set an HTTP filter for the network traffic and start your Perl script.
The very short gist of aspx pages it that they hold all of the local session information within a couple of variables prefixed by "__" in the general aspxform. Usually this is a top level form and all form elements will be part of it, but I guess that can vary by implementation.
For the particular implementation I was dealing with I needed to worry about 2 of these state variables, specifically:
__VIEWSTATE
__EVENTVALIDATION.
Your goal is to make sure that these variables are submitted into the form you are submitting, since they might be part of that main form aspxform that I mentioned above, and you are probably submitting a different form than that.
When a browser loads up an aspx page a piece of javascript passes this session information along within the asp server/client interaction, but of course we don't have that luxury with perl mechanize, so you will need to manually post these yourself by adding the elements to the current form using mechanize.
In the case that I just solved I basically did this:
my $browser = WWW::Mechanize->new( );
# fetch the login page to get the initial session variables
my $login_page = 'http://www.example.com/login.aspx';
$response = $browser->get( $login_page);
# very short way to find the fields so you can add them to your post
$viewstate = ($browser->find_all_inputs( type => 'hidden', name => '__VIEWSTATE' ))[0]->value;
$validation = ($browser->find_all_inputs( type => 'hidden', name => '__EVENTVALIDATION' ))[0]->value;
# post back the formdata you need along with the session variables
$browser->post( $login_page, [ username => 'user', password => 'password, __VIEWSTATE => $viewstate, __EVENTVALIDATION => $validation ]);
# finally get back the content and make sure it looks right
print $response->content();