Clearing User Session Data

Clearing User session data so changes in user permissions can take effect immediately.

By Bob Ray  |  December 14, 2023  |  2 min read
Clearing User Session Data

In my previous article, we looked at adding users to a User Group. When making changes to a user’s status or capabilities, sometimes you’d like the changes to take effect right away. Suppose, for example, that a user selects an option that adds them to a particular user group. It’s frustrating to find that the users have to log out, then log back in before MODX will treat them as members of the group.

Adding a user to a user group can have many ripple effects. It may qualify the user for one or more new permission ACL entries, which would mean more permissions for the user (potentially, both Context and object permissions). It might give the user access to Resources in protected Resource Groups, Elements in protected categories, or new Media Sources. There may also be Snippets that check the user’s status with $user->isMember().

In this article, we’ll look ways to force MODX to update the user’s status immediately. Susan Sottwell played an important role in testing the code below.

The Problem

You may have experienced problems with changes on your site not taking effect due to the cache. That’s not what we’re talking about here, though. Clearing the site cache often won’t help with updating a users’ status. That’s because much of the user’s information is held in the $_SESSION array. You could fix things by unsetting the $_SESSION array, but that would be a little like killing a mosquito with a sledgehammer. Deleting the _$SESSION effectively logs the user out and forces them to log back in, which is what we’re trying to avoid.

The Solution

The short answer is to refresh the user’s data with $user->loadAttributes(), though in some cases it requires an extra step. Here’s our code from the previous article with some extra lines that will make the change take effect immediately.

<?php
$groupname = 'Group1';
if(!($modx->user->isMember($groupname))) {
    $modx->user->joinGroup($groupname);

    /* New Code */
    $userId = $modx->user->get('id');
    $context = 'web';
    unset($_SESSION["modx.user.{$userId}.userGroups"]);
    $principalTargets = $modx->getOption('principal_targets', null, 'modAccessContext,
        modAccessResourceGroup,modAccessCategory,sources.modAccessMediaSource')
    $targets = explode(',', $principalTargets);
    array_walk($targets, 'trim');

    $modx->user->loadAttributes($targets, $context, true);
}

How It Works

The code could be made more efficient, but this version is a little easier to understand. Let’s look first at the loadAttributes() call. It takes three arguments.

The first argument is an array of permission-related classes that should be refreshed for the user ($principalTargets). Our code uses $modx->getOption() to retrieve the principal_targets System Setting. If it’s not set, we include a set of defaults that will take care of most situations: modAccessContext, modAccessResourceGroup, modAccessCategory, sources.modAccessMediaSource. These four (which make up the default value of the principal_targets System Setting) will cover refreshing the user’s access to the Context, Resource Groups, Categories, and Media Sources.

After the getOption() line runs, the targets will be a string containing a comma-separated list. We need it to be an array (that’s what loadAttributes() is expecting), so we convert it to one with explode(), which also removes the commas. To finish off the first argument, we use array_walk() to call the trim() function on each member of the array. The trim() function removes any leading or trailing spaces, newlines, or tabs.

The second argument to loadAttributes() is the context you want to be refreshed. This is usually web, but it could be another front-end context, or in very rare cases, the mgr context. This argument only takes a single context, but usually, you only need it to take effect in one context. If necessary, you could call loadAttributes() multiple times, once for each context.

If you have a multi-context site and the code could run in more than one front-end context, you can reload the user attributes for the current context by changing the $context = line to this:

$context = $modx->context->get('key');

The third argument, if set to true, tells MODX that you want things reloaded from scratch rather than from the cache.

Once we have the three arguments set up, we call loadAttributes() with them to refresh the user’s data.

What’s That Thing With the $_SESSION

There’s a minor bug in MODX (possibly fixed by the time you read this) that keeps the data from being completely refreshed. If you call user->joinGroup(), for example, that method clears a user $_SESSION variable called modx.user.##.userGroupNames (where ## is the user’s ID). At this writing, if you check the User’s Group membership with $user->getUserGroups() right after calling joinGroup(), the user won’t be a member yet because getUserGroups() checks this variable: $_SESSION["modx.user.##.userGroups"], which isn’t cleared by joinGroup().

To handle this problem, we’ve added this line to the code to clear the other $_SESSION variable:

unset($_SESSION["modx.user.{$userId}.userGroups"]);

I filed a pull request to fix this bug, so there’s a good chance that it will have been fixed by the time you read this, in which case that line is no longer necessary, though it won’t hurt anything to have it there.

Wrapping Up

Once we’ve cleared the user’s data with the code above, as soon as a method like isMember(), getGroups(), or hasPermission() is called, the data will be reloaded fresh from the database and the user will have the access they deserve.


Bob Ray is the author of the MODX: The Official Guide and dozens of MODX Extras including QuickEmail, NewsPublisher, SiteCheck, GoRevo, Personalize, EZfaq, MyComponent and many more. His website is Bob’s Guides. It not only includes a plethora of MODX tutorials but there are some really great bread recipes there, as well.