We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 23214
    • 36 Posts
    Hello,

    I have defined a custom access policy (called "Editors") on our client's website following this guide: https://rtfm.modx.com/revolution/2.x/administering-your-site/security/security-tutorials/giving-a-user-manager-access.

    The primary purpose of this is to prevent editors from breaking any programming elements (Templates, Chunks, Snippets). The editor also has permission to create/edit users, this allows them to manage user accounts signing up on the front-end.

    I'd like to know, is it possible to prevent an editor giving themselves super access? At present, an editor could edit their own account and assign themselves to the Administrator user group as a Super User. They could also delete the account of a Administrator/Super User.

    I've tested an editor account, and I'm able to increase my access level without restriction. Currently using MODX Revolution 2.4.2.

    thanks!
    Andrew (phatphug)

    This question has been answered by BobRay. See the first response.

      • 36818
      • 119 Posts
      It's not possible out of the box.

      You could try to use a plugin, e.g.
      <?php
      
      //injects a CSS, where you can hide fields
      if ($modx->event->name == 'OnUserFormPrerender')  {
      
      	$usergr = implode(', ', $modx->user->getUserGroups());
      
      if ($usergr == 1) {
        	//do nothing;
      
        } else {
        	$modx->regClientCSS('../assets/css/userAdminCSS.css');
      	}
      
      }
      //restrict listing of users to the group the user is in.
       if ($modx->context->get('key') == "mgr") {
          switch ($modx->event->name) {
            case 'OnMODXInit':
              $action = $modx->getOption('action', $_REQUEST, '');
              if ($action == 'security/user/getList') {
                $group = $modx->user->getPrimaryGroup();
                $_POST['usergroup'] = $group->get('id');
              }
              break;
          }
        }
      
      

      credits: http://modxcookbook.com/customize-manager/form-customization/restrict-users-list.html

      Remember to assign the plugin to the respective SystemEvents
        • 23214
        • 36 Posts
        Thanks for your reply achterbahn, that's a great idea!

        I've created a plugin which performs some checks whenever a user is created or deleted.

        FYI, I'm using the OnBeforeUserFormSave and OnBeforeUserFormDelete events for this. I then check the following actions:
        security/user/updatefromgrid
        security/user/update
        security/user/create
        security/user/delete

        This all works correctly, and I'm able to detect when a user is performing a nefarious task.

        The only problem I have is that I can't return a message to the user notifying them that their action has been blocked. I suppose plugins were never intended to work like this?

        Here's the basic structure of my code:
        $actionDenied = FALSE;
        
        // Perform lots of checks
        // ...
        
        // Deny action?
        if($actionDenied)
        {
            //$modx->sendUnauthorizedPage();
            //$modx->error->failure($modx->lexicon('access_denied'));
            $modx->sendRedirect("http://www.domain.com/manager");
            exit($modx->lexicon('access_denied'));
        }


        The only line of code which actually prevents the action is exit(). This is very crude and doesn't inform the user why their action failed. It simply halts the MODX manager in whatever state it was in without any details.
          • 3749
          • 24,544 Posts
          The only thing that comes to mind is to set a $_SESSION variable:

          $msg = "You don't have permission to delete a user";
          $_SESSION['AccessDeniedMessage'] = $msg';


          Then redirect to your own custom page where a snippet displays the error message:

          return $_SESSION['AccessDeniedMessage'];


          You could also pop up a JS message with runProcessor() and your own processor, but that would be significantly more complicated. It might work to just return the JS code for the popup, but I doubt it.
            Did I help you? Buy me a beer
            Get my Book: MODX:The Official Guide
            MODX info for everyone: http://bobsguides.com/modx.html
            My MODX Extras
            Bob's Guides is now hosted at A2 MODX Hosting
            • 23214
            • 36 Posts
            Thanks for your thoughts on this Bob.

            The problem is that I can't perform a redirect. Neither $modx->sendRedirect("http://www.domain.com/manager") or header("Location: http://www.domain.com/manager") will work. I don't see any errors in the manager or the log to indicate why.
            • discuss.answer
              • 3749
              • 24,544 Posts
              I've seen this before (the inability to redirect in a plugin). There may be a workaround, but I can't remember one.

              Both OnBeforeUserFormSave and OnBeforeUserFormDelete do use a returned value, so you might try one of these (where $msg is your error message) in your plugin:

              $modx->event->_output = $msg;
              return; // also try return false;



              return $msg;


              I think the first one is the correct method. The message may go to the error log, but it's possible that it will be displayed to the user.
                Did I help you? Buy me a beer
                Get my Book: MODX:The Official Guide
                MODX info for everyone: http://bobsguides.com/modx.html
                My MODX Extras
                Bob's Guides is now hosted at A2 MODX Hosting
                • 23214
                • 36 Posts
                Thanks Bob, this worked!

                Here's the final code fragment I'm using

                if($actionDenied)
                {
                    // Deny the action
                    // Thanks Bob! - https://forums.modx.com/thread/99968/restricting-modx-manager-access#dis-post-541494
                    $modx->event->_output = "This action was denied due to your access level. Please contact an administrator to make your changes.";
                    return;
                }


                And here's how it appears to the user.
                  • 3749
                  • 24,544 Posts
                  I'm glad you got it sorted. smiley

                  Could you post your plugin code? I'm thinking of making a blog post about controlling user's ability to deal with other users.
                    Did I help you? Buy me a beer
                    Get my Book: MODX:The Official Guide
                    MODX info for everyone: http://bobsguides.com/modx.html
                    My MODX Extras
                    Bob's Guides is now hosted at A2 MODX Hosting
                    • 23214
                    • 36 Posts
                    No problem at all Bob.

                    FYI, your posts on this forum and bobsguides.com have helped me countless times. Thanks for being such a major part of this community smiley

                    Here's my full plugin, I'm not sure if it follows best practices, but it works for me! Will be intrigued to see your post about this topic.

                    <?php
                    // mgrRestrictions plugin
                    // Prevents back-end users with access to the mgr from certain sensitive operations.
                    //
                    // This includes:
                    // 1. Creating/Saving any user which either has or would grant access to a privileged group.
                    // 2. Creating/Saving any user with the sudo flag.
                    // 3. Deleting any user which belongs to a privileged group..
                    //
                    // Should be executed on these events: OnBeforeUserFormSave, OnBeforeUserFormDelete
                    //////////////////////////////////////////////////////////////////////
                    
                    // Configuration - A list of privileged user groups (the array maps user group id to user group name).
                    // We will not allow these users to be edited/deleted by anyone other than an Administrator.
                    $privilegedGroups[1] = 'Administrator';
                    $privilegedGroups[3] = 'Editor';
                    $privilegedGroups[5] = 'Senior Editor';
                    
                    // For ease of use later, get a zero-index array of privileged user group id's and user group names.
                    $privilegedGroupsIds = array_keys($privilegedGroups);
                    $privilegedGroups = array_merge($privilegedGroups);
                    
                    // This only applies to the mgr context
                    if ($modx->context->get('key') == "mgr")
                    {
                        // Restrictions only apply to non-Administrators
                        if(!$modx->user->isMember('Administrator'))
                        {
                            // Get request parameters
                            $action = $modx->getOption('action', $_REQUEST, '');
                            $id = intval($modx->getOption('id', $_REQUEST, ''));
                    
                            // Perform checks based on event name
                            switch ($modx->event->name)
                            {
                                case 'OnBeforeUserFormSave':
                                case 'OnBeforeUserFormDelete':
                                    // Should we deny the action?
                                    $actionDenied = FALSE;
                    
                    
                                    // Updating a user from the grid
                                    //////////////////////////////////////////////////////////////////////
                                    if($action == "security/user/updatefromgrid")
                                    {
                                        // Fetch the json data
                                        $jsonData = $modx->getOption('data', $_REQUEST, '');
                                        if(!empty($jsonData))
                                        {
                                            // Decode to an array, we want an id
                                            $data = json_decode($jsonData, TRUE);
                                            if(!empty($data) && is_array($data) && array_key_exists('id', $data))
                                            {
                                                // Got a user id?
                                                $id = intval($data['id']);
                                                if($id > 0)
                                                {
                                                    $modUser = $modx->getObject('modUser', array('id' => $id));
                                                    if(!empty($modUser))
                                                    {
                                                        // Prevent privileged users from being edited
                                                        if($modUser->isMember($privilegedGroups))
                                                        {
                                                            $actionDenied = TRUE;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                    
                    
                                    // Updating a user (from a form) or creating a new user
                                    //////////////////////////////////////////////////////////////////////
                                    else if($action == "security/user/update" || $action == "security/user/create")
                                    {
                                        // Fetch the groups being applied to the user
                                        $jsonGroups = $modx->getOption('groups', $_REQUEST, '');
                                        if(!empty($jsonGroups))
                                        {
                                            // Decode to an array
                                            $groups = json_decode($jsonGroups, TRUE);
                                            if(!empty($groups) && is_array($groups))
                                            {
                                                // Check both the group id and group names (belt and braces!)
                                                foreach($groups as $group)
                                                {
                                                    // We expect to have a usergroup and name for each group
                                                    if(array_key_exists('usergroup', $group))
                                                    {
                                                        // Prevent privileged users from being edited
                                                        if(in_array(intval($group['usergroup']), $privilegedGroupsIds))
                                                        {
                                                            $actionDenied = TRUE;
                                                            break;
                                                        }
                                                    }
                                                    if(array_key_exists('name', $group))
                                                    {
                                                        // Prevent privileged users from being edited
                                                        if(in_array($group['name'], $privilegedGroups))
                                                        {
                                                            $actionDenied = TRUE;
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                        
                                        // Has sudo access been requested?
                                        $sudo = $modx->getOption('sudo', $_REQUEST, '');
                                        if(intval($sudo) === 1) {
                                            $actionDenied = TRUE;
                                        }
                                    }
                    
                    
                                    // Deleting a user from the grid or form
                                    //////////////////////////////////////////////////////////////////////
                                    else if($action == "security/user/delete")
                                    {
                                        // Got a user id?
                                        if($id > 0)
                                        {
                                            $modUser = $modx->getObject('modUser', array('id' => $id));
                                            if(!empty($modUser))
                                            {
                                                // Prevent administrators from being edited on the grid
                                                if($modUser->isMember($privilegedGroups))
                                                {
                                                    $actionDenied = TRUE;
                                                }
                                            }
                                        }
                                    }
                    
                    
                                    // Deny the action?
                                    //////////////////////////////////////////////////////////////////////
                                    if($actionDenied)
                                    {
                                        // Debug
                                        $modx->log(modX::LOG_LEVEL_ERROR, "mgrRestrictions(".$modx->event->name.") - The following action (requested by ".$modx->user->get('username')." #".$modx->user->get('id').") was denied ($action, $id)\r\n".print_r($_REQUEST, TRUE));
                                        
                                        // Deny the action
                                        // Thanks Bob! - https://forums.modx.com/thread/99968/restricting-modx-manager-access#dis-post-541494
                                        $modx->event->_output = "This action was denied due to your access level. Please contact an administrator to make your changes.";
                                        return;
                                    }
                                break;
                            }
                        }
                    }
                    ?>
                      • 23214
                      • 36 Posts
                      Perhaps this is slightly off topic, but one slightly irritating thing I've found with MODX is that allowing editors to create/edit users requires some unintuitive permissions like:

                      • namespaces (To edit or view Namespaces.)
                      • settings (To view and edit any System Settings.)

                      Without those permissions, whenever an editor edits a user, they'll see an error popup:
                      Code: 200 OK
                      {"success":false,"message":"Permission denied!","total":0,"data":[],"object":[]}



                      I believe this happens because the settings tab requires a list of namespaces and settings. It would be nice if I could deny those permissions without a popup error appearing. Or if there was a permission which would hide the entire settings tab.



                      Maybe this is something addressed in later versions of MODX, this website is still on 2.4.2.

                      (FYI modified post to include inline screenshots rather than attachments)