Implementing standard PHP form validation within a Slim application - php

So I’m trying to implement php server side validation for a registration form. I’m using the slim framework along with twig templates to display the html. I’ve been trying to use this example to achieve this but I think the way I have set up my slim routing is interfering with error messages displaying properly on the registration form.
The way my application works currently is that a user navigates to the registration form page which presents the registration form. This is is shown here:
$app->get('/register', function(Request $request, Response $response)
{
return $this->view->render($response,
'register.html.twig',
[
'css_path' => CSS_PATH,
'landing_page' => $_SERVER["SCRIPT_NAME"],
'action_register' => './register/success',
//'initial_input_box_value' => null,
'page_title' => 'Login App - Register',
]);
})->setName('register');
The 'action_register' => './register/success', represents the form ‘action’ attribute in the html twig form and indicates where and how the form data is processed and the response to the submitted form. This can be seen here: (note that the first few lines of the register/success page is the example used from the guide i’ve been using for php validation)
$app->post('/register/success', function(Request $request, Response $response) use ($app)
{
$nameError = " ";
if(isset($_POST['submit']))
{
if (empty($_POST["username"])) {
$nameError = "Name is required";
} else {
$name = test_input($_POST["username"]);
// check name only contains letters and whitespace
if (!preg_match("/^[a-zA-Z ]*$/",$name)) {
$nameError = "Only letters and white space allowed";
}
}
}
function test_input($data)
{
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
$arr_tainted_params = $request->getParsedBody();
$sanitizer_validator = $this->get('validate_sanitize');
$password_hasher = $this->get('hash_password');
$tainted_email = $arr_tainted_params['email'];
$tainted_username = $arr_tainted_params['username'];
$tainted_password = $arr_tainted_params['password'];
$model = $this->get('model');
$sql_wrapper = $this->get('sql_wrapper');
$sql_queries = $this->get('sql_queries');
$db_handle = $this->get('dbase');
$cleaned_email = $sanitizer_validator->validate_email($tainted_email);
$cleaned_username = $sanitizer_validator->validate_username($tainted_username);
$cleaned_password = $sanitizer_validator->validate_password($tainted_password);
$hashed_cleaned_password = $password_hasher->hash_password($cleaned_password);
$model->set_user_values($cleaned_username, $cleaned_email, $hashed_cleaned_password);
$model->set_sql_wrapper($sql_wrapper);
$model->set_sql_queries($sql_queries);
$model->set_db_handle($db_handle);
if ($sanitizer_validator->get_validate_messages() == ' ')
{
$model->store_user_details();
$_SESSION["loggedin"] = true;
$_SESSION["username"] = $cleaned_username;
$arr_storage_result_message = '';
echo $sanitizer_validator->get_validate_messages(); //this will be turned into a proper alert prompt at a later date
}
if(isset($_SESSION["loggedin"]) && $_SESSION["loggedin"] === true)
{
return $this->view->render($response,
'display_user.html.twig',
[
'css_path' => CSS_PATH,
'landing_page' => $_SERVER["SCRIPT_NAME"],
//'action_register' => 'index.php/register',
//'initial_input_box_value' => null,
'page_title' => 'Login App - Display',
'username' => $cleaned_username,
'hashed_password' => $cleaned_password,
]);
}
});
The idea is that the user creates an account and is then logged in whereby the 'display_user.html.twig', is displayed. Otherwise error messages regarding form validation should be displayed on the registration form itself. However the registration form is only displayed on the /register page. But the registration form posts the data and processes it on the registration/success page.
So I’m not sure if the php validation guide code is in the wrong place or that my slim routing is incorrect for this purpose of form validation. I think the problem lies in the fact that when the form is submitted it redirects trhe user to a new page namely register/success.
In summary i’m trying to implement php form validation such as this within a slim application using twig templates.
I’ll also include the register.html.twig page of the registration form if that helps:
{% extends 'header_footer.html.twig'%}
{% block content %}
<h3>Register A New Account</h3>
<form method = "post" action = " {{ action_register }} ">
<p>Email: <input type="text" name="email" ><br></p>
<p>Username: <input type="text" name="username" ><br></p>
<span class="error">* <?php echo $nameError;?></span> //code used in the php validation guide
<p>Password: <input type="text" name="password" ><br></p>
<!--<p>Password Confirm: <input type="text" name="password_confirm"><br></p> THIS WILL BE IMPLEMENTED LATER-->
<input type="submit" value="Create Account">
</form>
{% endblock %}

You can't use PHP in Twig like that.
Take a look at these:
Slim-Flash
slim-twig-flash
Slim-Validation
With Flash and Slim-Validation you can achieve your desired error messages.
If you don't want to use the above components then you could simply pass your $nameError variable to the template:
PHP:
return $this->view->render($response,
'display_user.html.twig',
[
'nameError' => $nameError, // HERE
'css_path' => CSS_PATH,
'landing_page' => $_SERVER["SCRIPT_NAME"],
//'action_register' => 'index.php/register',
//'initial_input_box_value' => null,
'page_title' => 'Login App - Display',
'username' => $cleaned_username,
'hashed_password' => $cleaned_password,
]
);
Twig:
{% if nameError %}<span class="error">* {{ nameError }}</span>{% endif %}

Related

How to get posted value from different function?

I am using codeigniter 3.1 .
How to return or get posted email after submit data from different function?
HTML
<form action="settings/valid" class="form-horizontal" enctype="multipart/form-data" method="post" accept-charset="utf-8">
<input type="email" name="email" value="<?php echo $email ?>" />
<input type="submit"/>
</form>
PHP
public function index() {
// how to get post email ?
$email = $this->input->post("email"));
$this->template->loadContent("settings/index.php", array(
"email" => $email
)
);
}
public function valid() {
$email = $this->input->post("email"));
$this->user->add($this->user->ID, array(
"email" => $email
));
redirect(site_url("index.php"));
}
This may answer your question better.
public function index() {
// how to get post email ?
$email = $this->session->flashdata("email");
$this->template->loadContent("settings/index.php", array(
"email" => $email
)
);
}
public function valid() {
$email = $this->input->post("email"));
$this->user->add($this->user->ID, array(
"email" => $email
));
$this->session->set_flashdata("email",$email);
redirect(site_url("index.php"));
}
What you are doing is called routing. That means that it makes no sense to get the email in index, because no data has been sent to index. What you can do is redirect the user to index and pass a post argument to index.
To put it another way. Let's say you open a web page and give it your name. You cannot expect another page to know your name as well, because that page does not exist yet (it has not been generated yet by php).
But you can send the user to another page and tell that page the user's name.
For instance, in your case:
in valid()
redirect(site_url("index.php?email=".$email));
and in index()
$email = $this->input->get("email")

Symfony3 Forms: How to know which button was clicked in the form?

Symfony3 Forms: I have managed to build and render the form as shown below:
<form action="/member/john/actions" method="post" name="form">
<input type="submit" value="Block John" name="block">
<input type="submit" value="Remove from my friends" name="remove">
<input type="hidden" value="LeiajURspTa9c8JEUYtvepki0b_CdL9dMWqEZxOYvfk" name="form[_token]" id="form__token">
</form>
When I click the the buttons "Block John" or "Remove from my friends", the controller routes it to the desired location (member_friend_actions) and it is able to show the debug dump values along with "Submitted!" text, before dying.
My controller with route "member_friend_actions" is set-up as shown below:
/**
* A common post location to catch all operations like add/remove/cancel/block friends
*
* #Route("/{username}/actions", name="member_friend_actions")
* #Method("POST")
*/
public function allActionsFriendAction(Request $request, User $friend)
{
$form = $this->createAllActionsFriendForm($friend);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//$clicked = $form->getData();
$clicked = $form->getClickedButton()
\Doctrine\Common\Util\Debug::dump($clicked);
die("Submitted!");
}
return $this->redirectToRoute('member_profile', array("username" => $friend->getUsername()));
}
I want to know which button was clicked that brought it here (block or remove here; but there can be more buttons in other places). I tried using the methods:
$form->getData() => which gives array(0) { } and
$form->getClickedButton() => gives NULL, so are not helping.
How can this be achieved?
It depends how you added the SubmitType to your form.
For example if you used something like this:
->add('block_john', SubmitType::class, array(
'label' => 'Block John'
))
Then in your controller you can then use something like:
$form->get('block_john')->isClicked()
See this link for more info:
http://symfony.com/doc/current/form/multiple_buttons.html
Since I did not create all the buttons at once using ->add() notation, but were produced with if-else-if-else conditions and calling corresponding methods as shown below:
$form = $this->createCancelFriendForm($user);
$addFriendForm = $form->createView();
perhaps this was reason that function like $form->getData(), $form->getClickedButton(), $form->get('block')->isClicked() returned empty values.
But because this form was still getting through the validation and into successfully Submitted case; I simply tried using the if(isset($_POST["block"])) {} equivalent of Symfony like so:
if ($form->isSubmitted() && $form->isValid()) {
if ($request->request->get("block")) {
// Pass it over to corresponding block handler
$response = $this->forward('AppBundle:Member:blockFriend', array(
'friend' => $friend,
));
return $response;
}
if ($request->request->get("unblock")) {
// Pass it over to corresponding unblock handler
$response = $this->forward('AppBundle:Member:unBlockFriend',
array(
'friend' => $friend,
));
return $response;
}
// I can add n number of other checks ( eg: add, remove, accept)
// here and add their handlers as above
}
...which worked for me as desired. B)

Token issue when attempting to login laravel

So I have a login function posted below that works great, no issues most of the time. However on the homepage for whatever reason if you try and login it does not authenticate you and changes the url to the following
http://localhost/?_token=Jh36AX6sx0qhzOniPoMSn3pROCAVombCn4xKzoJm&email=admin%40admin.com&password=testpass
This makes no sense to me, it throws some weird error and shows the email and password in plain text. Does anyone know what is going on? I use the exact same function on any other page and it works fine, but not the homepage. The only other time I have seen the url do something similar is when setting up and doing password reset stuff. Any ideas?
public function doLogin()
{
// validate the info, create rules for the inputs
$rules = array(
'email1' => 'required|email', // make sure the email is an actual email
'password1' => 'required|alphaNum|min:3' // password can only be alphanumeric and has to be greater than 3 characters
);
// run the validation rules on the inputs from the form
$validator = Validator::make(Input::all(), $rules);
// if the validator fails, redirect back to the form
if ($validator->fails())
{
return Redirect::to('login')->withErrors($validator)->withInput(Input::except('password1')); // send back the input (not the password) so that we can repopulate the form
}
else
{
// create our user data for the authentication
$userdata = array(
'email' => Input::get('email1'),
'password' => Input::get('password1')
);
// attempt to do the login
if (Auth::attempt($userdata))
{
return Redirect::to('profile');
}
else
{
$this->layout->content = View::make('login')->with('p', 'Incorrect Login Information');
}
}
}
As requested this is the form that is calling the function, it is using POST
{{ Form::open(array('url' => 'login', 'class' => 'form form-horizontal' ,'method' => 'post')) }}
{{ Form::text('email1', Input::old('email1'), array('class'=>'form-control','placeholder' => 'Email Address')) }}
{{ Form::password('password1', array('class'=>'form-control','placeholder' => 'Password')) }}
<button type="submit" class="btn btn-primary form-control">Login</button><br><br>
{{ HTML::link('login/fb', 'Sign-in with Facebook',array('class' => 'btn btn-primary form-control facebook')) }}
{{ HTML::link('password/remind', 'Forgot Password?') }}
{{ Form::close() }}
It appears your form is using GET instead of POST. I would recommend posting your form code to verify this. When you switch it to POST, make sure your routes are setup to handle it as a POST request as well.

not passing validation - Symfony2 Form without a Class

After a thorough search im not seeing the problem.
I have a form im submitting, which isnt tied to an object, its just an emailer form. I want to validate the data. according to the docs the alternative way is to do this.
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
$builder
->add('firstName', 'text', array(
'constraints' => new Length(array('min' => 3)),
))
->add('lastName', 'text', array(
'constraints' => array(
new NotBlank(),
new Length(array('min' => 3)),
),
))
;
now thats done, but i CANNOT call $form->isValid on this in any shape or form (pun intended), because even though it passes the constraint violations, something seemingly invisible is still causing it to return invalid.
i feel like i might need to extract the form from the post first, and pass that through isValid() but i cant be sure.
heres my method code
/**
* #Route("/share", name="email_share")
* #Method({"POST"})
* #return \Symfony\Component\HttpFoundation\Response
*/
public function shareAction( Request $request, array $lessons){
if(!$lessons || !is_array($lessons))
{
throw new HttpException(404, "Whoops! shareAction didnt get the lessons");
}
//set up our form defaults here
$defaultData = array('comment' => 'Type your comment here');
//build the form here
$form = $this->createFormBuilder($defaultData)
->setAction($this->generateUrl('email_share'))
->add("emails", 'email', array(
'label' => "Recipient's email address (separate with a comma)",
'constraints' => array(
new Length(array('min' => 6, 'max' => 2040)),
new NotBlank(),
),
))
->add('comment', 'textarea', array(
'label' => "Leave a comment",
))
->add('name', 'text', array(
'label' => "Your name",
'constraints' => array(
new Length(array('min' => 3), 'max' => 254)),
new NotBlank(),
),
))
->add('email', 'email', array(
'label' => "Your email address",
'constraints' => array(
new Length(array('min' => 6, 'max' => 254)),
new NotBlank(),
),
))
->add('copy', 'checkbox', array(
'label' => "Send me a copy",
'required' => false,
))
->add('cancel', 'submit', array(
'label' => "Cancel",
))
->add('save', 'submit', array(
'label' => "Email Resources",
))
->getForm();
if ($this->getRequest()->isMethod('POST')) {
//data is already validated by constraints added when the form was created since we are not attaching this particular form to any object
$form->handleRequest($request);
//alternatively (makes no differene from the former)
//$form->submit($request->request->get($form->getName()));
if ($form->isValid())
{
//have YET to see this
echo 'valid';
exit;
}
else{
//echo 'not fuckin valie, WHY?';
//exit;
// get a ConstraintViolationList
$errors = $this->get('validator')->validate( $form );
$result = '';
//nothing returns here when form is valid against constraints, its just empty
echo $errors;
// iterate on it
foreach( $errors as $error )
{
$error->getPropertyPath() : the field that caused the error
$error->getMessage() : the error message
}
}
$data = $form->getData();
return $this->emailUser($data);
}
return $this->render('ResourceBundle:Default:resources.html.twig', array(
'form' => $form->createView(),
));
}
This is how im posting the data
function postForm($form, callback) {
/*
* Get all form values
*/
var values = {};
$.each($form.serializeArray(), function (i, field) {
values[field.name] = field.value;
});
/*
* Throw the form values to the server!
*/
$.ajax({
type: 'POST',
url: '/share',
data: values,
success: function (data) {
callback(data);
}
});
}
$(document).ready(function () {
//bind an event to submit on 'email resources' button
$('div#share form').submit(function (e) {
//disable symfonys default submit button hehaviour
e.preventDefault();
postForm($(this), function (response) {
//replace html here
// Is this where im going wrong? Do i need to replace the form here?
});
});
});
EDIT: Here is the pertinent portion of the main template code that calls the action in the first place
<div id="share" class="hidden" >
<h2>Share Resources</h2>
{% render url('email_share') %}
</div>
here is the form template code thats rendered in the shareAction (in its entirety currently)
{{ form(form) }}
was
{% if form | default %}
{{ form(form) }}
{% endif %}
{% if mail_response | default %}
{{ dump(mail_response) }}
{% endif %}
The hidden token input portion of the form
<input id="form__token" class="form-control" type="hidden" value="8QWLo8xaPZFCKHBJbuc6CGNIcfmpWyT-yFdWScrsiJs" name="form[_token]">
The two underscores worry me a bit (form__token)
EDIT2:
The problem is in the CSRF token somewhere. it could be the form input name, the token itself is already expired, or something else.
I pretty much narrowed it down by constructing my own form module like this
//set up our form defaults here
$defaultData = array('comment' => 'Type your comment here');
$session = new Session();
$secret = '123xyz';
$vendorDir = realpath(__DIR__ . '/../vendor');
$vendorFormDir = $vendorDir . '/symfony/form/Symfony/Component/Form';
$vendorValidatorDir =
$vendorDir . '/symfony/validator/Symfony/Component/Validator';
// create the validator - details will vary
$validator = Validation::createValidator();
$formFactory = Forms::createFormFactoryBuilder()
->addExtension(new HttpFoundationExtension())
//->addExtension(new CsrfExtension(new SessionCsrfProvider($session, $secret)))
->addExtension(new ValidatorExtension($validator))
->getFormFactory();
//build the form here
$form = $formFactory->createBuilder('form', $defaultData)
->setAction($this->generateUrl('email_share'))
->setMethod('POST')
->add("emails", 'email', array(
//......
//same as above for the rest......
The form FINALLY passes validation like this, and when i uncomment the line
->addExtension(new CsrfExtension(new SessionCsrfProvider($session, $secret)))
i get the same error as i did before, that the CSRF token is invalid.
To me, this is pretty much pointing to somewhere in this module, or im not calling something, or extending something right, or the javascript is returning a form that is older than the CSRF module is expecting, or the hidden token form input has a name that is other than that what the CSRF module is looking for. I dont know enough about symfony's internals to diagnose this, this is why i come here for help. Does anybody see a potential issue?
EDIT:3 i feel like i shouldnt be using isValid(), as mentioned, i am not passing an object, im passing an array. see this URL http://symfony.com/doc/current/book/validation.html#validating-values-and-arrays. Im trying to figure out how to properly check against the constraints, and im thinking isValid() is NOT the way to go after all, or else im missing something fundamental.. I just cant figure if i only check against constraint errors, how can i use the CSRFprotection still, or is that only for objects or something?? Do i need to pass this in manually since im not using an object?
EDIT 4:
It looks like i might have uncovered the crux of the problem, yet i cant figure out how to solve it yet.
on this file Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider
i put some output to track the token, and it appears that the token is regenerating for the comparison, which seems like IT SHOULD NOT be the case I would expect that it would compare the passed down token to one in memory, but in fact, its being generated twice, once for the form, then once again for the comparison.
At first i suspected it was possible that the browser and the ajax are running from two different sessions, and a mismatch can be caused by this because i was using SessionCsrfProvider(), but after switching to ->addExtension(new CsrfExtension(new DefaultCsrfProvider($secret))) i had the same problem.
Is this a bug, am i going crazy, or am i missing something as simple as the form id in the building of the form or something?
heres the code, and the results i found from that code.
//Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider
public function isCsrfTokenValid($intention, $token)
{
echo '<pre>Warning, Symfony\Component\Form\Extension\Csrf\CsrfProvider\isCsrfTokenValid';
echo'<br>, here is out token handed down to compare<br>';
var_dump($token);
echo '<br>the new generated token thats being compared to is<br>';
var_dump($this->generateCsrfToken($intention));
echo '</pre>';
return $token === $this->generateCsrfToken($intention);
}
returns
//the form
array(6) { ["emails"]=> string(19) "email#email.net"
["comment"]=> string(2) "yo" ["name"]=> string(5) "me"
["email"]=> string(19) "email#email.net" ["copy"]=>
string(1) "1" ["_token"]=> string(40)
"a11e10eb323f7a4d19577e6d07e68be951ceb569" }
Warning,
Symfony\Component\Form\Extension\Csrf\CsrfProvider\isCsrfTokenValid ,
here is out token handed down to compare string(40)
"a11e10eb323f7a4d19577e6d07e68be951ceb569"
the new generated token thats being compared to is string(40)
"e83cdf94b15e63e822520b62402eb66e0b1f03d3"
The CSRF token is invalid. Please try to resubmit the form.
Blockquote
EDIT 5:
the problem has been traced to here, look at this code in the DefaultCsrfProvider
public function generateCsrfToken($intention)
{
return sha1($this->secret.$intention.$this->getSessionId());
}
public function isCsrfTokenValid($intention, $token)
{
return $token === $this->generateCsrfToken($intention);
}
The token can never be valid during an ajax call, unless a param is set in the generateCsrfToken() token method to allow passing of the session, to which you would want to pass that via ajax, like this
public function generateCsrfToken($intention, $session)
{
if(!$session)
{
$session = $this->getSessionId()
}
return sha1($this->secret.$intention.$session);
}
which i would think would completely reduce teh security of the whole idea of the CSRF in the first place.
is there another provide i can use for ajax calls in within the symfony framework? If were depending on the sesion, this pretty much leaves out both the SessionCsrfProvider class and the DefaultCsrfProvider class to process this, unless im missing something very obvious... should i just grab, pass, then reset the session on the ajax call????
Ok, after i figured this out thus far, i just found this post Symfony CSRF and Ajax ill see if i can make heads or tails from it.
To see errors you should render that form. In your code when form is invalid the method returns $this->emailUser($data); but it should render the form. Try this:
if ($this->getRequest()->isMethod('POST')) {
//data is already validated by constraints added when the form was created since we are not attaching this particular form to any object
$form->handleRequest($request);
//alternatively (makes no differene from the former)
//$form->submit($request->request->get($form->getName()));
if ($form->isValid())
{
//have YET to see this
echo 'valid';
//this is the place to process data
//$data = $form->getData();
//return $this->emailUser($data);
exit;
}
}
return $this->render('ResourceBundle:Default:resources.html.twig', array(
'form' => $form->createView(),
));
That should render invalid form again on submit and show error
You can also try this
if ($form->isValid())
{
//have YET to see this
echo 'valid';
exit;
}else{
echo '<pre>';
\Doctrine\Common\Util\Debug::dump($form->getErrorsAsString(), 9);
echo '</pre>';
}
To show errors in debug style if form is invalid.

Splitting form into two pages causes error

I have setup the laravel resource controller and utilized the edit and update methods to edit user profiles. My profile form turned out to be too long, so I would like to split it into two forms.
The trouble is that the update function appears to be built into the resource controller - I tried just copy the method, add in my inputs and rename it. I updated the routes and view, but received an error. I also tried to have both forms call the same function, but the information that wasn't included in the form was delete from my db.
My question is, how do I split my form into two, so I can update my user profile from two forms instead of one? Any help would be appreciated. Thank you
For reference, here is my ContractorController
public function edit($id)
{
//
// get the contractor
$contractor = Contractor::find($id);
// show the edit form and pass the contractor
return View::make('contractors.edit')
->with('contractor', $contractor);
}
public function update($id)
{
//
// validate
// read more on validation at http://laravel.com/docs/validation
$rules = array(
);
$validator = Validator::make(Input::all(), $rules);
// process the login
if ($validator->fails()) {
return Redirect::to('contractors/' . $id . '/edit')
->withErrors($validator)
->withInput(Input::except('password'));
} else {
// store
$contractor = Contractor::find($id);
$contractor->name = Input::get('name');
$contractor->tag_line = Input::get('tag_line');
$contractor->contact_name = Input::get('contact_name');
//would like to split items below into a separate form:
$contractor->public_email = Input::get('public_email');
$contractor->phone = Input::get('phone');
$contractor->address_1 = Input::get('address_1');
$contractor->city = Input::get('city');
$contractor->state = Input::get('state');
$contractor->zip = Input::get('zip');
$contractor->website = Input::get('website');
$contractor->story = Input::get('story');
$contractor->save();
// redirect
Session::flash('message', 'Successfully updated profile!');
return Redirect::to('contractors');
}
}
Start of form in edit.blade.php
{{ Form::model($contractor, array('route' => array('contractors.update', $contractor->id), 'class' => 'form-horizontal', 'method' => 'PUT')) }}

Categories