Field $valid state is not updating after $setValidity(True) - validation

I'm using ui-validate for my validation rules.
<input ng-model=raw.expression
name="expression"
type="text"
required
ui-validate="{serversidevalid : 'validate_serverside($value)'}">
The validation is executed server side, the short version ... in the controller
$scope.validate_serverside = function(expression) {
someValidationFactory
.validate(expression)
.then(function(server_error_message) {
var validity = true,
error_message = false,
input = $scope.my_form['expression'];
if (server_error_message !== undefined) {
validity = false;
error_message = server_error_message;
}
input.$setValidity('serversidevalid',validity);
input.$error.serversidevalid = error_message;
});
Now, after this function is executed with a valid expression - The form's status is valid, The $error key and message are as exepected but the field's $valid / $invalid remains invalid.
How is it possible for the entire form to become valid but the field to remain invalid ?
I tried wrapping the two last statements with $scope.$apply or explicitly trigger $scope.$digest() but the it throws an "digest already in progress". I tried wrapping the thing with $timeout to execute after the digest is done - same undesired result.
appreciate some guidance here.

So I came across this discussion on github and after a quick look at the code I relaized that as of the last couple of month ui-validate support server side validation via promises.
So I dropped the external manipulation of $setValidity and changed my validation as follows
$scope.validate_serverside = function(expression) {
var deferred = $q.defer();
someValidationFactory
.validate(expression)
.then(function(server_error_message) {
if (server_error_message !== undefined)
deferred.reject(false)
else
deferred.resolve(true)
});
return deferred.promise;
}

Related

Do I need to verify the result for Google Invisible reCaptcha

I am following the instructions on this page
to implement the invisible recaptcha. Everything works great, but how do I know it is working? Is there a way to force a false to test it?
Also, The documentation is not clear on the above page, but some places have additional code to verify the users "response" (it's invisible so i'm not sure what the response is here) - so do I need to add additional back end logic to hit this endpoint with the invisible reCaptcha resulting token and my secret key?
What happens when the user clicks submit on the invisible recaptcha? What is done in the API to return the token? What is the token for? What does the siteverify api then do to determine its a person? Why isnt additional verification needed when the reCAPTCHA V2 (visible click one) is used?
After some testing it looks like you could just do the front end part. The data callback function is not called until google is sure you are a person, if google is not sure then it loads the "select which tiles have a thing in them" reCaptcha to be sure. Once the reCaptcha api is sure that it is a person, the data callback function is fired - at that time you can do further validation to ensure that the token you received during the callback is the one that google actually sent and not a bot trying to fool you by hitting your callback funct - so from there you do server side processing for further validation. Below is an example of a C# ashx handler - and ajax for the validation
function onTestSubmit(token) {
$.ajax({
type: "POST",
url: "testHandler.ashx",
data: { token: token },
success: function (response) {
if (response == "True") {
//do stuff to submit form
}
}
});
}
And the ashx
public class testHandler : IHttpHandler {
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/plain";
string token = context.Request.Form["token"];
bool isCaptchaValid = ReCaptcha.Validate(token);
context.Response.Write(isCaptchaValid.ToString());
}
public bool IsReusable {
get {
return false;
}
}
}
public class ReCaptcha
{
private static string URL =
"https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}";
private static string SECRET = "shhhhhhhhhhhhhhSecretTOken";
public bool Success { get; set; }
public List<string> ErrorCodes { get; set; }
public static bool Validate(string encodedResponse)
{
if (string.IsNullOrEmpty(encodedResponse)) return false;
var client = new System.Net.WebClient();
var googleReply = client.DownloadString(string.Format(URL, SECRET, encodedResponse));
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var reCaptcha = serializer.Deserialize<ReCaptcha>(googleReply);
return reCaptcha.Success;
}
}
Yes, you do.
You need to understand that invisible reCaptcha is a process with multiple steps, all of which end up providing a final response regarding the humanity of the user.
In simple words, when the users submits a form (or does whatever it is you are trying to keep bots away from with Invisible reCaptcha) you'll be sending your public sitekey to your backend, which will fire up a verification payload to Google.
In my very basic example, this is the button which the hopefully human visitor clicks to submit a form on my site:
<button type="submit" class="g-recaptcha" data-sitekey="xxxxxxxx_obscured_xxxxxxxx" data-callback="onSubmit">Submit Form</button>
Note how the the button has the data-callback "onSubmit", which upon submission runs this small script:
<script type="text/javascript">
var onSubmit = function(response) {
document.getElementById("simpleForm").submit(); // send response to your backend service
};
</script>
The backend service in my example is a vanilla PHP script intended to process the form input and store it on a database and here comes the tricky part. As part of the POST to the backend, besides the form fields the user filled is the response from the service (and since you may or may not be doing a lot of things on the front-end where the user could manipulate the response before it's posted to your backend, Google's response is not explicit at this point)
On your backend, you'll need to take g-recaptcha-response that came from google and post it to a verification API using your private key (which is not the one on the form) in order to get the human/robot verdict which you can act upon. Here's a simple example written in PHP and hitting the API with cURL:
$recaptcha_response = $_POST["g-recaptcha-response"];
$api_url = 'https://www.google.com/recaptcha/api/siteverify';
$api_secret = 'zzzzzzz_OBSCURED_SECRET_KEY_zzzzzzzzzzz';
$remoteip = '';
$data = array('secret' => $api_secret, 'response' => $recaptcha_response);
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data)
)
);
$context = stream_context_create($options);
$result = file_get_contents($api_url, false, $context);
$captcha_response = json_decode($result, true);
// at this point I have the definite verdict from google. Should I keep processing the form?.
if ($captcha_response['success'] == true) {
// I heart you, human. Keep going
$captcha_error = 0;
}
else {
// Damn robot, die a slow and painful death
$captcha_error = 1;
}
I make a final decision based on $captcha_error (basically 1 means halt, 0 means keep processing)
If you rely solely on getting the g-recaptcha-response you're having Google do the work and then ignoring the result

Bootstrap Fileinput does not send file again on second upload

When I upload a file with Krajees Bootstrap Fileinput, I perform a server side validation of the file. When something goes wrong, I output a JSON-Object simply with {error:'Something went wrong'}. The Plugin displays the error perfectly.
But then: When I press again "upload" just after that, the $_FILES array in the called submit PHP script is empty. This means, the plugin does not send the file again even if it has notified that an error has occurred.
Why would the plugin only upload the file once even if it detects that there was an error? Are there any methods that can "reset" the "uploaded state" of the file? (I'm only uploading one file).
I already checked the file events but none of them brought me to the desired result, instead they kind of destroyed the whole upload form with certain buttons being suddenly disabled and so on.
I finally found out the exact point, where the problem could be solved:
On line 1705 in the function updateUploadLog, the function self.updateStack is called. This call simply clears the file stack and causes a later process to empty the form input. Simply commenting out this line does the trick, but only if you reload after success, because somehow fnSuccess is also called when an error is found.
#Angad thank you very much for your solution triggering input, thanks to that I found a place to start the search again ;)
I see that the Github Issue says this isn't currently supported, but it seems relatively uncomplicated to fork this project and bend it to your needs. All the fnError ='s you'll find in a Cmd + F search inside fileinput.js are where you need to look.
Take for instance here: https://github.com/kartik-v/bootstrap-fileinput/blob/d5ed3ee989edbd5d67b8cf4bdadc9f3c18609965/js/fileinput.js#L1897
This is for the batch file-upload that currently looks like this:
fnError = function (jqXHR, textStatus, errorThrown) {
var outData = self._getOutData(jqXHR), errMsg = self._parseError(jqXHR, errorThrown);
self._showUploadError(errMsg, outData, 'filebatchuploaderror');
self.uploadFileCount = total - 1;
if (!self.showPreview) {
return;
}
self._getThumbs().each(function () {
var $thumb = $(this), key = $thumb.attr('data-fileindex');
$thumb.removeClass('file-uploading');
if (self.filestack[key] !== undefined) {
self._setPreviewError($thumb);
}
});
self._getThumbs().removeClass('file-uploading');
self._getThumbs(' .kv-file-upload').removeAttr('disabled');
self._getThumbs(' .kv-file-delete').removeAttr('disabled');
};
I'd try modifying this to:
fnError = function (jqXHR, textStatus, errorThrown) {
if (!myError.equals(textStatus)) { // A service-like impl. injection would be sexier
var outData = self._getOutData(jqXHR), errMsg = self._parseError(jqXHR, errorThrown);
self._showUploadError(errMsg, outData, 'filebatchuploaderror');
self.uploadFileCount = total - 1;
if (!self.showPreview) {
return;
}
self._getThumbs().each(function () {
var $thumb = $(this), key = $thumb.attr('data-fileindex');
$thumb.removeClass('file-uploading');
if (self.filestack[key] !== undefined) {
self._setPreviewError($thumb);
}
});
self._getThumbs().removeClass('file-uploading');
self._getThumbs(' .kv-file-upload').removeAttr('disabled');
self._getThumbs(' .kv-file-delete').removeAttr('disabled');
} else {
self._ajaxSubmit(fnBefore, fnSuccess, fnComplete, function() {
// TODO: Second time failure - handle recursively or differently? :-)
);
}
};
Hope this helps!

How to prevent additional page requests after response sent

I have configured a listener on kernel.request which sets a new response with redirect when the session time has reached a certain value. The listener works fine and redirects to a certain page, on the next request, after the session has ended. But my problem is on the page I have many links and if I press multiple times the same link, the initial request with the redirect is cancelled/stopped and a new request is made with the last link pressed and so it passes my redirect even though the session has ended and is destroyed. So, my question is how to prevent additional requests/link presses after the firs request is made?
Here is my code:
public function onKernelRequestSession(GetResponseEvent $event)
{
$request = $event->getRequest();
$route = $request->get('_route');
$session = $request->getSession();
if ((false === strpos($route, '_wdt')) && ($route != null)) {
$session->start();
$time = time() - $session->getMetadataBag()->getCreated();
if ($route != 'main_route_for_idle_page') {
if (!$session->get("active") && $route == 'main_route_for_site_pages') {
$session->invalidate();
$session->set("active", "1");
} else {
if ($time >= $this->sessionTime) {
$session->clear();
$session->invalidate();
$event->setResponse(new RedirectResponse($this->router->generate('main_route_for_idle_page')));
}
}
} else {
if ($session->get("activ")) {
$session->clear();
$session->invalidate();
}
}
}
}
Thak you.
Idea #1: Simple incremental counter
Each request sends sequence number as param which is being verified as expected at the server.
Server increments the number and sends it back via response
the new number is used in future requests
Basically, if server expects the SEQUENCE number to be 2 and client sends 1 the request is to be rejected.
Idea #2: Unique hash each time
Similar to the idea above, but uses unique hashes to eliminate predictive nature of incremental sequence.
I resolved the issue using JQuery: when a link was pressed I disabled the other ones and so only one request is made from the page:
var isClicked = false;
$(".menu-link").click(function(e) {
if(!isClicked) {
isClicked = true;
} else {
e.preventDefault();
}
});
Thanks.

How can I solve the warning "Warning: array_key_exists."?

I'm using Hybridauth social login, and upon a user authenticating with Facebook, I receive the following error:
Warning: array_key_exists() [function.array-key-exists]: The second
argument should be either an array or an object in
/hybridauth/Hybrid/thirdparty/Facebook/base_facebook.php on line 1328
My guess (probably wrong) to why this may be happening is because the parameters used to pass to Hybridauth come from the browser URL, and I have two - page=register & connected_with=facebook. Hybridauth only requires the second one...
It actually authenticates, but I want rid of this error. Why does this warning occur? Is there a way to hide it?
This is the bit that errors:
/**
* Get the base domain used for the cookie.
*/
protected function getBaseDomain() {
// The base domain is stored in the metadata cookie
// if not we fallback to the current hostname
$metadata = $this->getMetadataCookie();
if (array_key_exists('base_domain', $metadata) &&
!empty($metadata['base_domain'])) {
return trim($metadata['base_domain'], '.');
}
return $this->getHttpHost();
}
It's this code the warning comes from:
/**
* Destroy the current session
*/
public function destroySession() {
$this->accessToken = null;
$this->signedRequest = null;
$this->user = null;
$this->clearAllPersistentData();
// JavaScript sets a cookie that will be used in getSignedRequest
// that we need to clear if we can
$cookie_name = $this->getSignedRequestCookieName();
if (array_key_exists($cookie_name, $_COOKIE)) {
unset($_COOKIE[$cookie_name]);
if (!headers_sent()) {
$base_domain = $this->getBaseDomain();
setcookie($cookie_name, '', 1, '/', '.'.$base_domain);
} else {
// #codeCoverageIgnoreStart
self::errorLog(
'There exists a cookie that we wanted to clear that we couldn\'t '.
'clear because headers was already sent. Make sure to do the first '.
'API call before outputting anything.'
);
// #codeCoverageIgnoreEnd
}
}
}
It looks like getMetadataCookie() does not always return an array, possibly because the cookie has not yet been set. You may want to check that it's actually an array before using it as such;
if (is_array($metadata) && array_key_exists('base_domain', $metadata) &&
For the added code, the same would apply to array_key_exists() in the new code. If you're unsure if it's actually set to an array if the cookie is not set, check first.

CI Route Issue Not Getting Variable

I'm having an issue with this route and not sure what my problem is exactly.
My page is located at http://www.kansasoutlawwrestling.com/kowmanager/pmsystem/viewmessage/1 where 1 is the message id.
I set up a route to look like
$route['pmsystem/viewmessage/(:num)'] = 'pmsystem/viewmessage/$1';
and I'm still getting a error message like this
A PHP Error was encountered
Severity: Warning
Message: Missing argument 1 for Pmsystem::viewmessage()
Filename: controllers/pmsystem.php
Line Number: 76
// View A Message
function viewmessage($message_id)
{
//Config Defaults Start
$msgBoxMsgs = array();//msgType = dl, info, warn, note, msg
$cssPageAddons = '';//If you have extra CSS for this view append it here
$jsPageAddons = '<script src='.base_url().'../assets/js/cpanel/personalmessages.js></script><script src='.base_url().'assets/js/mylibs/jwysiwyg/jquery.wysiwyg.js></script>';//If you have extra JS for this view append it here
$metaAddons = '';//Sometimes there is a need for additional Meta Data such in the case of Facebook addon's
$siteTitle = '';//alter only if you need something other than the default for this view.
//Config Defaults Start
//examples of how to use the message box system (css not included).
//$msgBoxMsgs[] = array('msgType' => 'dl', 'theMsg' => 'This is a Blank Message Box...');
/**********************************************************Your Coding Logic Here, Start*/
// Checks to see if a session is active for user and shows corresponding view page
if (!$this->loggedin->chkLoginStatus() === FALSE)
{
if( ! $this->uri->segment(3))
{
redirect('error', 'refresh');
}
}
else
{
redirect('login', 'refresh');
}
$bodyContent = 'viewpm';//which view file
$bodyType = "full";//type of template
/***********************************************************Your Coding Logic Here, End*/
//Double checks if any default variables have been changed, Start.
//If msgBoxMsgs array has anything in it, if so displays it in view, else does nothing.
if(count($msgBoxMsgs) !== 0)
{
$msgBoxes = $this->msgboxes->buildMsgBoxesOutput(array('display' => 'show', 'msgs' =>$msgBoxMsgs));
}
else
{
$msgBoxes = array('display' => 'none');
}
if($siteTitle == '')
{
$siteTitle = $this->metatags->SiteTitle(); //reads
}
//Double checks if any default variables have been changed, End.
$this->data['msgBoxes'] = $msgBoxes;
$this->data['cssPageAddons'] = $cssPageAddons;//if there is any additional CSS to add from above Variable this will send it to the view.
$this->data['jsPageAddons'] = $jsPageAddons;//if there is any addictional JS to add from the above variable this will send it to the view.
$this->data['metaAddons'] = $metaAddons;//if there is any addictional meta data to add from the above variable this will send it to the view.
$this->data['pageMetaTags'] = $this->metatags->MetaTags();//defaults can be changed via models/metatags.php
$this->data['siteTitle'] = $siteTitle;//defaults can be changed via models/metatags.php
$this->data['bodyType'] = $bodyType;
$this->data['bodyContent'] = $bodyContent;
$this->data['user_data'] = $this->users->getUserByUserId($this->session->userdata('user_id'));
$this->data['users'] = $this->loggedin->getUserList();
$this->data['personal_messages'] = array($this->pmmodel->getTotalMessages($this->session->userdata('user_id')), $this->pmmodel->getTotalUnreadMessages($this->session->userdata('user_id')), $this->pmmodel->getLast5Messages($this->session->userdata('user_id')));
$this->data['messages'] = array($this->pmmodel->getInboxMessages($this->session->userdata('user_id')), $this->pmmodel->getSentMessages($this->session->userdata('user_id')));
//$this->data['message_data'] = $this->pmmodel->getPmMessage($this->uri->segment(3));
$this->load->view('cpanel/index', $this->data);
}
UPDATE
// Checks to see if a session is active for user and shows corresponding view page
if (!$this->loggedin->chkLoginStatus() === FALSE)
{
if (!is_numeric($this->uri->segment(3)))
{
$this->data['message_data'] = 'Invalid message id!';
}
else
{
$this->data['message_data'] = $this->pmmodel->getPmMessage($this->uri->segment(3));
}
$bodyContent = 'viewpm';//which view file
}
else
{
redirect('login', 'refresh');
}
$bodyType = "full";//type of template
This route is unnecessary - it doesn't change anything.
$route['pmsystem/viewmessage/(:num)'] = 'pmsystem/viewmessage/$1';
You can remove that route. The problem is here:
function viewmessage($message_id) // no default value means it's required
{
// your code
}
Your controller methods literally accept user input as arguments (whatever's in the address bar). You always have to account for those required arguments not being present in CI controller methods.
function viewmessage($message_id = NULL)
{
if ( ! $message_id) show_404();
// your code
}
This will silence the errors and show a 404 if the required $message_id is not there. Additionally, $this->uri->segment(3) is unnecessary because it should have the same value as $message_id.
I highly discourage redirecting to an error page when you really want a 404, but that's up to you. It sure doesn't help the user realize their mistake when the address is lost after the redirect, and you're sending the wrong HTTP headers by doing so.

Resources