Jump to content

Validation Checks Comparing Fields' Values


Midnight Modding
 Share

Go to solution Solved by bfarber,

Recommended Posts

What is the best way to do a validation check for one $value in relation to another when one may not even be on the form at all and someone could sneak it into the url to affect request data?

function( $val ) use( $item, $leader )
{
	if( $leader == '2' && $val == \IPS\Request::i()->team_leader )

Notice I did that $leader check because that is the same check that determined if 'team_leader' was to show on the form at all. Are my only options repeating the same checks like that or unsetting request data they may have snuck into the url to affect the form?

I haven't checked to see when the form data is put into request, either. If at the time of me doing field1's validation check, not ALL form fields have been put into request data, then even if all fields ARE supposed to be on the form, the form data would not have overwritten url data of them sneaking values into the url to bypass.

When you click submit, and it redirects to the same page again, does it automatically retain all data from the original url, or if I unset some request data, will it still be unset on the redirect to itself? If unsetting it keeps it from remaining in the url on submit, i could unset some potential sneaky url request data as a solution.

Edited by Midnight Modding
Link to comment
Share on other sites

Am I understanding correctly that you have two fields, and you want to validate one as it relates to another? So, a random example, if we are choosing a member we need to ensure we have a member ID but if we are choosing a group we need to ensure we have a group ID? You can either add a custom validation function that checks \IPS\Request::i()->form_field_name, or you can check in the if( $values = $form->values() ) loop and then set $form->error (and not save/redirect) if the value is invalid.

Link to comment
Share on other sites

9 hours ago, bfarber said:

Am I understanding correctly that you have two fields, and you want to validate one as it relates to another? So, a random example, if we are choosing a member we need to ensure we have a member ID but if we are choosing a group we need to ensure we have a group ID? You can either add a custom validation function that checks \IPS\Request::i()->form_field_name, or you can check in the if( $values = $form->values() ) loop and then set $form->error (and not save/redirect) if the value is invalid.

Pretty much, except a wrinkle to it is: fieldB will be on the form always. fieldA may be on it or may not, depending on checks when creating the form. In the validation check for fieldB, it needs to error out if fieldA is a particular value, but fieldA is not always on the form. So what i was worried about is someone throwing fieldA=2 into the url and thus forcing it into \IPS\Request::i() to affect checks.

Or even for fields always on the form, what if they put fieldA=2 in the url and there is a fieldA on the form, also, with 2 not being an option? When does fieldA from the url get overwritten by the one from the form? Just worries me about checking \IPS\Request::i() values if url monkeying can mess with it, which it can for sure if it's a filed not meant to show on the form at all.

At least if I do it in the loop you're referring to, it would already be in values. Not sure if $form->error is a true error only or shows the form again, but since those checks would be only for monkeying, it wouldn't matter. I do still wonder when the form data is in \IPS\Request::i(), though. ie if it's already populated before even the first validation check and overwrites url request data with the same keys as soon as you click submit.

Edited by Midnight Modding
Link to comment
Share on other sites

  • Solution

\IPS\Request::i() values are populated immediately at runtime from GET/POST before the form handler is ever loaded. Every single form in the software can be monkeyed with technically, so while you are right to consider this while writing your validation function, it is not a unique problem.

If fieldA is presently and has options of 1, 2 or 3 and someone submits a value of 4 (by adjusting the URL or submitting a direct POST request), the form handler will automatically throw an error and $form->values() will return FALSE, because the value is not an acceptable value in the $options that you pass.

What you will need to do in your validation function is

  • Determine if fieldA was shown or not (and not by checking the request, but by using the same logic you use to determine to show it or not in the first place)
  • If it was shown, then check the Request object for its value. If it is an invalid value, then the built in check for fieldA will already error out so you don't need to worry _too_ much here about an invalid value.
  • If it was not shown, then ignore any value sent for it.
Link to comment
Share on other sites

7 hours ago, bfarber said:

\IPS\Request::i() values are populated immediately at runtime from GET/POST before the form handler is ever loaded. Every single form in the software can be monkeyed with technically, so while you are right to consider this while writing your validation function, it is not a unique problem.

If fieldA is presently and has options of 1, 2 or 3 and someone submits a value of 4 (by adjusting the URL or submitting a direct POST request), the form handler will automatically throw an error and $form->values() will return FALSE, because the value is not an acceptable value in the $options that you pass.

What you will need to do in your validation function is

  • Determine if fieldA was shown or not (and not by checking the request, but by using the same logic you use to determine to show it or not in the first place)
  • If it was shown, then check the Request object for its value. If it is an invalid value, then the built in check for fieldA will already error out so you don't need to worry _too_ much here about an invalid value.
  • If it was not shown, then ignore any value sent for it.

Thanks. The only thing I am not 100% clear on is if the value of \IPS\Request::i()->fieldA can change between runtime and the validation checks. I think you're saying no because GET/POST data itself would have a value for it at runtime and that data would have already overwritten one with the other and it doesn't get populated with anything new during the form validation.

I've never even tested before to see if you put it in the url AND enter something on a form for the same key, which overwrites which (even as GET/POST). :) Seems like maybe i tried in 3.x and the form values overwrote the url ones, but I can't remember for sure. But as long as \IPS\Request::i() is unchanged during the form processing itself, I can easily do all I need with your above suggestions. Like you said, then if the value is a disallowed one, the $form->values() check will take care of any potential tricks.

Edited by Midnight Modding
Link to comment
Share on other sites

Blah. I just realized something else. I assume request data for dates (which I do need to compare several of) will be strings instead of DateTime objects, so checks will require duplicate object creations, since suite will auto do it later.

The easiest on a lot of this would be to do it in the $values = $form->values() loop, but I haven't tested yet to see if $form->error still shows the form or if it is an error where the form is gone. My guess is it shows the form and puts some error message above it instead of under an input?

In the next day or two, I'll test and get it all sorted. This is just a massive app, taking me forever, so that's why I sometimes ask things on here, with the hopes someone has time to answer before I get around to having to test. :)

Edited by Midnight Modding
Link to comment
Share on other sites

No, you don't need to use die() or exit() either. Here's pseudo-code to show you what I mean

$form = new \IPS\Helpers\Form;
$form->add( /* Add form elements */ );

if( $values = $form->values() )
{
	// Let's say you've found an error
	$error = TRUE;

	// Here we see if an error was found. If so, set the error message on the form.
	if( $error == TRUE )
	{
		$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'error_message_lang_key' );
	}
	else
	{
		// No error was found, so save and redirect
		\IPS\Output::i()->redirect( ... );
	}
}

// You will reach this point EITHER if an error was found OR if the form has not been submitted yet
\IPS\Output::i()->output = (string) $form;

 

Link to comment
Share on other sites

I knew not to use die() or exit() myself. I was just basically saying thanks for reminding me that the $values loop would be continuing due to $form->error not being a situation that would use those. i was going to do a check just like what you showed in this last post, but if you hadn't reminded me, I may have forgot the $values loop would be continuing and kept my save() in. lol.

Edited by Midnight Modding
Link to comment
Share on other sites

  • 1 month later...

I wish there would be a built in way of comparing multiple form values on submit. Having to check other values with \IPS\Request means they haven't been formatted, so I am going to have to do the same work that the form helper will already do, myself, to be able to compare one field to another. ? Or I guess I could do Brandon's suggestion... at least that way they will already be formatted, but then the error will be at the top instead of for a specific field. probably still the best way, at this point, though.

I guess I'll just decide on a case by case basis. For some of them it wouldn't be so horrible... just a simple intval or create().

Edited by Midnight Modding
Link to comment
Share on other sites

  • 3 months later...

off topic, a bit, but felt like this was too minor of a question to warrant a whole topic/qa.

Why do values from a form Select input get automatically cast as string? I don't even see where IPS does it, as Select.php doesn't have a formatValue() method.

In other words, for my Select, the choices were in an array where the keys were values from database int columns. But once in $values, their choice is considered a string.

I then thought that meant that the database values themselves were not ints in the php, so went and changed some === to ==, only to realize I should have just tested first because the database column values are treated as ints. It's just when you use them as keys for the Select, they become strings in the validation process.

(edit: ps, on topic of what this was originally about, I successfully got all of that done... sometimes using request, sometimes doing checks in the $values = $form->values() part).

Edited by Midnight Modding
Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...