Hi,

i have some problems with ajaxified forms when i used authcache. I have a form that uses ajax to submit data and when i obtain no data from the ajax call. The problem is in includes/ajax.inc, function ajax_get_form() tries to obtain the form from cache using: form_get_cache, that function validates form token stored in the form cache, but the token is different for each user, so nothing is returned.

Can someone give me a tip to avoid this problem?

Best
David

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

znerol’s picture

Would you mind installing the Developer Examples and check whether the form / ajax examples work on your site?

david.gil’s picture

Hi Lorenz,

i have installed in a clean drupal install authcache + examples and the problem are still there.

What i did is:

1.- create 2 users
2.- with one of them access:

http://localhost:8082/examples/ajax_example/submit_driven_ajax

ajax is working...

3.- access with other user and ajax is not working. The problem is that forms cannot be obtained from form cache, see includes/ajax.inc function ajax_get_form (line: 323). In form_get_cache (form.inc), the system checks the validity of cache_token of the form (token is generated in common.inc drupal_get_token) and that token includes session_id, so i think the problem is there...

any ideas what to do?
thx in advance
David

david.gil’s picture

Hi,

one workaround that i am working in is use a custom callback to avoid this form token validations, as is done in views. See:

function views_ui_ajax_get_form($form_id) {

in views_ui.module.

In my form i added a path to the #ajax command:


$form['experiencia']['actions']['submit'] = array(
            '#id' => "experiencia-submit",
            '#type' => 'submit',
            '#prefix' => '<div class="enviar">',
            '#suffix' => '</div>',
            '#attributes' => array('class' => array('medium', 'blue', 'nice', 'button', 'radius', 'form-submit', 'f-override')),
            '#value' => t('Enviar'),
            '#ajax' => array(
                'event' => 'click',
****>         'path' => 'ajax/forms/experiencia',
                'callback' => 'dk_experiencias_ajax_submit_callback',
                'wrapper' => 'form_nueva_experiencia_wrapper',
                'effect' => 'fade',
                'method' => 'html'
            ),
        );

And a menu callback that reproduces system/ajax like views do.

function dk_experiencias_menu(){
  $items['ajax/forms/experiencia'] = array(
    'title' => 'AHAH callback',
    'page callback' => 'dk_experiencias_ajax_form_callback',
    'delivery callback' => 'ajax_deliver',
    'access callback' => TRUE,
    'theme callback' => 'ajax_base_page_theme',
    'type' => MENU_CALLBACK,
  );
    return $items;
}

This is the callback:


function dk_experiencias_ajax_get_form($form_id) {
  // @see ajax_get_form()
  $form_state = array(
    'no_redirect' => TRUE,
  );
  $form_state['rebuild_info']['copy']['#build_id'] = TRUE;
  $form_state['rebuild_info']['copy']['#action'] = TRUE;
  $form_state['input'] = $_POST;

  $form = drupal_build_form($form_id, $form_state);
  // @see ajax_form_callback()
  if (!empty($form_state['triggering_element'])) {
    $callback = $form_state['triggering_element']['#ajax']['callback'];
  }
  if (!empty($callback) && function_exists($callback)) {
    return $callback($form, $form_state);
  }
}

The main problem is security, without a token validation this could be a security hole.

What do you think?

Best
David

znerol’s picture

Actually authcache is supposed to fetch the form-token using an AJAX call after the page is built. Would you please try to verify whether the authcache AJAX-call is executed immediately after the page is loaded? See the attached screenshot for how this should look like when using chrome network inspector.

IMHO dropping the form-token is only an option when dealing with forms triggering idempotent actions (i.e. which do not alter any content). For example search.

Update: Forgot to mention, I was not able to reproduce the misbehavior. The AJAX-examples all work well here on a fresh install with authcache 7.x-2.x-dev.

david.gil’s picture

Hi again Lorenz,

i install a really fresh drupal 7.21.

Installed authcache 2.x dev, and enabled: authcache, autcache_debug & authcache_ajax
In settings only has authcache cache backend.

Again i have the same problems: You can reproduce it as i show in the screenshot:

1.- I create 2 different authenticated users. I request examples page using one of then.

2.- Enter in other browser with the other user, and obtain the cached page. As you can see tokens are retrieved ok, but when i click ajax button i obtain blank response cause i report previously.

Best

znerol’s picture

An empty response normally indicates a problem on the server side. Would you please read through the apache-error log and check whether there are PHP errors reported? Perhaps you also find something in the drupal watchdog logs?

znerol’s picture

Only to be sure. Do you have both of the following lines in your settings.php?

$conf['cache_backends'][] = 'sites/all/modules/authcache/modules/authcache_ajax/authcache_ajax.inc';
$conf['cache_backends'][] = 'sites/all/modules/authcache/authcache.inc';
david.gil’s picture

Hi lorenz,

yes cache backends are ok in settings.
- no php errors.

- in watchdog i have errors, reported by ajax_get_form, the problem is there:


function ajax_get_form() {
  $form_state = form_state_defaults();

  $form_build_id = $_POST['form_build_id'];

  // Get the form from the cache.
  $form = form_get_cache($form_build_id, $form_state);
  if (!$form) {
    // If $form cannot be loaded from the cache, the form_build_id in $_POST
    // must be invalid, which means that someone performed a POST request onto
    // system/ajax without actually viewing the concerned form in the browser.
    // This is likely a hacking attempt as it never happens under normal
    // circumstances, so we just do nothing.
    watchdog('ajax', 'Invalid form POST data.', array(), WATCHDOG_WARNING);
    drupal_exit();
  }

If you debug: (includes/form.inc).

function form_get_cache($form_build_id, &$form_state) {
  if ($cached = cache_get('form_' . $form_build_id, 'cache_form')) {
    $form = $cached->data;

    global $user;
    if ((isset($form['#cache_token']) && drupal_valid_token($form['#cache_token'])) || (!isset($form['#cache_token']) && !$user->uid)) {

You will see that in common.inc is where token validation for form is check, and it gives me two different values:

/**
 * Validates a token based on $value, the user session, and the private key.
 *
 * @param $token
 *   The token to be validated.
 * @param $value
 *   An additional value to base the token on.
 * @param $skip_anonymous
 *   Set to true to skip token validation for anonymous users.
 *
 * @return
 *   True for a valid token, false for an invalid token. When $skip_anonymous
 *   is true, the return value will always be true for anonymous users.
 */
function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) {
  global $user;

print($token);
print(" -- " . drupal_get_token($value));

  return (($skip_anonymous && $user->uid == 0) || ($token == drupal_get_token($value)));
}
znerol’s picture

Category: support » bug

Ok, I understand now whats going on. I will need some time to investigate whether it is legible to drop #cache_token from ajax forms when they are cached for the first time.

As a workaround i suggest you to cancel page caching whenever your custom ajax form is rendered:

if (module_exists('authcache')) {
  authcache_cancel(t('Custom AJAX form'));
}

Alternatively you may exclude the pages where your form is rendered by listing the respective paths in the authcache page ruleset.

Thank you for reporting this issue and for giving such detailed information. I appreciate that very much.

david.gil’s picture

Hi,

as a workaround i use what i said, i implement a custom ajax url callback that avoids this problem, it has a security issue but i can asume it. I cannot disable cache for that pages, cause they are the core of our app.

I wait your answer, if i can help you say me!.

Best

znerol’s picture

Just as david.gil pointed out, the problem is that a per-user form-token is saved along with with a form to the form cache when a user is logged in (see form_set_cache. Upon retrieval of a form from the form-cache, the token is checked (form_get_cache. Therefore authcache currently does not work with cached forms. This is especially annoying because ajax forms require the form-cache.

In order to solve the problem I've implemented an additional authcache ajax command which simply retrieves a given form from the form-cache, generates a new build-id and stores it back into the cache on behalf of the current user. The new build-id is then injected into the dom. You find the code in the attached patch, its not in the repository yet.

This is somewhat a proof of concept. I guess that there may occur some problems when the page cache and form cache bins are invalidated at different times. For example if a form-cache entry is purged before the respective page from the page cache, the ajax command will not be able to clone that form and the form will break. Form cache entries expire after 6 hours (hardcoded in form_set_cache). Perhaps its possible to prevent the cache bins from getting out of sync by ensuring that the caches are flushed more often than every 6 hours.

In order to make the new ajax command available, one needs to rebuild the authcache ajax registry. E.g. by issuing the following drush command:

drush ev '_authcache_ajax_rebuild();'
david.gil’s picture

Hi lorenz,

seems that in the patch is mixed: includes/AuthcacheFormBuildIdCommand.inc

best

znerol’s picture

Sorry for that. New patch attached.

znerol’s picture

Fixed in 9735682

znerol’s picture

Status: Active » Fixed

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

pdendis’s picture

Issue summary: View changes

The attached patches does not apply on 7.x-2.x-dev and on 7.x-2.0-beta3. It's for different versions

pdendis’s picture

Status: Closed (fixed) » Active
znerol’s picture

Status: Active » Closed (fixed)

This specific patch has been committed to the repository more than a year ago (see commit 9735682. However it was subsequently discovered that the underlying problem was a Drupal core issue. It has been fixed in Drupal 7.27 and Authcache 7.x-2.0-beta3. Additionally please make sure that Cache Object API is installed and configured properly.

Please open a new issue if things still do not work even though everything is configured properly.