i'm trying to use user_access() to test the permissions of all the users on the system, not the user viewing the page. basically, as part of the administrative interface to a module i'm working on, i need to create a drop-down box of all the users that have a given permission. so, the administrative page itself uses user_access() to make sure the viewing user has admin rights for my module, but i need to call user_access() again for all the other users to see if they should be in the drop-down or not.

i see that user_access() takes a second argument, an "$account" user object, which mostly does what i need. however, i noticed that user_access() itself only ever uses the "uid" field from the account object. sadly, calling user_load() for each user seems awfully expensive (multiple DB queries, user_module_invoke(load), etc, etc), especially since user_access() doesn't need anything except the uid. i've already got all the usernames and uids from a previous DB query, and i'd really like to avoid N wasted calls to user_load()...

so, i've written a small patch to user.module which moves the existing user_access() functionality that does the real checking into a method called "user_access_uid()". this method takes a perm string and the uid. the main user_access() method is now just a thin wrapper around user_access_uid() to deal with pulling the uid out of either the passed in account (if specified), or the global $user object (if $account is NULL). therefore, user_access() continues to work as it always has, but now there's another method module authors can use to do efficient access checking if they already know the uids.

if there's a better way to do what i'm trying to do, i'd love to hear it.

thanks!
-derek

p.s. my entire site is running 4.6, so that's where i'm doing all of my development. i just tried to apply the same patch to the HEAD version of user.module and it failed. the db_query() in the HEAD version of user_access() was re-written such that it now uses the "roles" field of the $account object as part of a WHERE clause instead of using an INNER JOIN. so, i guess user_access() is a little more efficient now in the HEAD for the default case, which is good, but it makes this whole user_access_uid() thing a bit of a lost cause. :( in the HEAD, i think i'd have to pass in the "roles" field, too, and if that was NULL, use the old INNER JOIN query, and if we had a value, it could use the newer query with just the WHERE. i can see that's going to be more complicated, and therefore, less likely to happen. drat.

but, my original problem remains.... can i do what i want without the fairly major cost of calling user_load for every user on my system? i can't imagine i'm the first person to encounter this problem, but my searches on drupal.org have come up empty.

CommentFileSizeAuthor
user_access_uid.patch2.58 KBdww
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

dww’s picture

thanks to the wonders of cvs annotate and how well-organized drupal.org and the drupal source repository is, i found the thread that led to the change in db_query() that causes this patch to fail on HEAD: http://drupal.org/node/44379. just wanted to provide as much info as possible in here for whomever reads this.

killes@www.drop.org’s picture

Status: Needs review » Needs work

We don't accept new features for the stable 4.6 branch. It is also too late for non-critical features for 4.7. I suggest you commit this as a patch to cvs after development for 4.8 opens up.

Crell’s picture

In the meantime, you can get your admin interface by simply doing a single query hit before hand.

$result = db_query("SELECT uid FROM users");
while ($record = db_fetch_object($result)) {
  user_access('perm', $record);
  ...
}

(Or something like that.)

As long as it's a PHP object, the user_access() function doesn't demand that it be a complete user object, just that it have a property of uid.

magico’s picture

Version: 4.6.5 » x.y.z
pwolanin’s picture

Version: x.y.z » 6.x-dev

since dww just referenced this on the devel list...

cburschka’s picture

This patch looks pretty straightforward for re-rolling, except for the db_query on the account roles, as mentioned. Since user_access now uses $account->roles and not just $account->uid, the full object is once again required.

Question being: What causes more overhead, loading the entire user module for an access check, or JOINING on the roles table?

cburschka’s picture

Sorry, of course I meant "loading the entire user object".

chx’s picture

Version: 6.x-dev » 7.x-dev
Jaza’s picture

Status: Needs work » Closed (fixed)

I agree with @Arancaytar in #6 (above): the user object is once again required, because $account->roles needs to be populated. Whether you populate $account via user_load(), or manually via your own SQL query, is up to you (the latter may be more efficient in some situations).

Closing.