Rules won't work properly when run during cron, if you use node access restrictions

I’ve recently created USPS tracking module for Drupal, so Qwintry.com users could get notifications when their international packages change state. I’ve used queue operations to build requests to USPS API by cron, and it seems to work great for our customers, but this story is not about the module.

My plan was to provide rules event “The package [tracking number] changed active state from [old state] to [new state]”. (words in square brackets are Rules arguments).
On Qwintry website, “Package” is a node, with “tracking number” textfield.

So, basically, my Rule was:

if [tracking number] changed active state from [old state] to [new state]:
   - fetch entity by property "tracking number" = [tracking number]  
   - send [author of loaded entity] a nice email about state change

Easy, huh?

I’ve implemented _rules_event_info() hook in my module code, and created my Rule.
The rule worked perfectly when I triggered the event using my admin account, but..

.. it didn’t work when I run website cron to trigger my event.

event was triggered but it couldn’t find the node with such property (though I knew that I have node with such tracking number in db).

After hours of debugging I found out that the issue here is that “Fetch entity by property” action of Rules uses EntityFieldQuery, which of course respects node access permissions and checks current user access. My “package” nodes were private for their owners. And cron uses anonymous user to trigger the event! So, cron didn’t have enough permissions to load the nodes.

That makes perfect sense (for lots of use cases!) to check node access unconditionally, but in my case it was a big trouble.
I think that the perfect solution for the issue would be an “ignore access permissions” checkbox in “fetch entity by property” Rules action.

I’ve created an issue in Rules issue queue: http://drupal.org/node/1804586 but didn’t get any replies yet.

I hope this will save some time to someone else!

Comments

Anton,

If you schedule the rule using Rules Scheduler instead of using a cron event, do you get the desired results? At the end of your rule, you can reschedule the rule for a relative time like +12 hours.

Scott

20 October, 2012

Haven’t tried it - will try soon.

21 October, 2012

I had a similar problem in drupal 6 with a rule that would set up a rules schedule to add or remove a taxonomy term to a node. I was using tax access control and was having issues. I ended up getting it to work by modifing my crontab to run cron with drush instead of hitting cron.php with wget. Im on my phone right now when i get home i wil update my post with the line i have in my crontab.

20 October, 2012

This is what I had in my crontab with the correct info filled in for root and uri.
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0,15,30,45 * * * * /usr/bin/drush --root=/path/to/your/drupalroot --uri=drupalsite.org --quiet -u 1 cron

Adding -u 1 forces drush to run cron with the admin account instead of anonymous.

Some more info: http://drush.ws/docs/cron.html

21 October, 2012

That’s a good and elegant solution, thanks for sharing!
Though disabling access checks completely for cron makes me a bit nervous.. it should be used carefully.

21 October, 2012

Maybe you can add special system role for cron with custom permissions.

22 October, 2012

But running as uid=1 isn't the right solution; it would be better if you could choose the scheduled task to run as:
a) uid=0 (anonymous)
b) uid=1
c) the uid of the user who triggered the task.

To do so rules should save the uid of the user who triggered the task (I don't know if it already does), and the scheduled task should run between this function calls:

function mymodule_su($uid = 1) {
global $user;
if (!array_key_exists('mymodule_su', $_SESSION)) {
$_SESSION['mymodule_su'] = array();
}
array_push($_SESSION['mymodule_su'], $user);
session_save_session();
$user = user_load($uid);
}

function mymodule_unsu() {
global $user;
if (array_key_exists('mymodule_su', $_SESSION)) {
if (!empty($_SESSION['mymodule_su'])) {
$user = array_pop($_SESSION['mymodule_su']);
}
if (empty($_SESSION['mymodule_su'])) {
unset($_SESSION['mymodule_su']);
}
}
}

18 January, 2013

Hey I'm not sure whether or not it's me or possibly your web site but it's launching undoubtedly slowly for me, it took me for a minute or two
in order to load up although google operates totally .

However , thank you for publishing beautiful article.
I do believe it has already been honestly helpful individual
who visit here. This one is without a doubt
fantastic what you have done and would like to check
out nice posts from you. I already have you
bookmarked to see blog you publish.

Here is my web blog; how to earn money online get paid
cash for free surveys (http://refnearn.com/)

01 November, 2014

Post new comment

Private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options

Note for potential spammers: all links in your comment will not be indexed by search engines.

Anton Sidashin

Anton Sidashin senior developer, Pixeljets co-founder

I'm a web developer specializing in PHP and Javascript, and Drupal, of course. I'm building Drupal projects since 2005, and I was working as full-time senior engineer in CS-Cart for a while, building revolutionary e-commerce software. In my free time, I enjoy playing soccer, building my body in gym, and playing guitar.

Drupal.org ID: restyler
Drupal association member