We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 22303 MODX Staff
    • 10,725 Posts
    Quote from: BobRay at Aug 10, 2008, 02:37 PM

    That will be great. While you’re at it, you might consider changing the names of some of the files. There are a number of different login.php and login.js files. The one that actually does the authentication tests might better be called something like loginproc.php. Some comments at the beginning about who calls us and what we do wouldn’t hurt either.

    Does this make sense? Pull the actual authentication tests out of loginproc.php and make it an object with a "register authentication" method that lets people register as many authentification techniques as they like, each with test code, a failure message, and a priority number (to determine what order the tests are made).
    The location of the files determines what purpose they serve; it makes more sense to me to keep the various parts of the same feature in files of the same name at the appropriate location. With that in mind, you are talking about the login processor in the model, i.e. core/model/modx/processors/security/login.php, which provides a single interface for login supported by a plugin event framework for external authentication, as well as the option for providing derivative modUser classes to extend/override just about any part of user behavior, and configure them by Context.

    If some of the functionality in this login processor needs to be refactored and moved into a plugin, or additional configuration settings need to be added, we can do that. If we need a new event, we can do that (though I think we have plenty in there), etc.

    We can already give plugins a priority, so it shouldn’t be a problem doing that. Ultimately, I think everything we need is there, users can provide their own custom processors, their own custom authentication plugins, and much more.

    I’ll review it some more, but in the meantime, here is an example plugin for simple authentication/user integration with Crowd (via SOAP) I already have working; I’ll be releasing more of this component very soon, along with the new MODx web site infrastructure built on Revolution.

    <?php
    switch ($modx->event->name) {
        case "OnUserNotFound":
            if (isset($username) && !empty($username)) {
                $crowdAttributes = array (
                    'url' => isset($crowdUrl) ? $crowdUrl : $modx->config['user.crowd.url'],
                    'application' => isset($crowdApp) ? $crowdApp : $modx->config['user.crowd.application'],
                    'credential' => isset($crowdPwd) ? $crowdPwd : $modx->config['user.crowd.credential']
                );
                $modx->addPackage('modx.user.crowd', MODX_CORE_PATH . 'model/');
                if ($crowd = $modx->getService('crowd', 'modCrowdClient', '', $crowdAttributes)) {
                    $userexists = $crowd->findUsername($username);
                    $user = & $scriptProperties['user'];
                    $user = $modx->newObject('modCrowdUser');
                    $user->set('username', $username);
                    $user->addOne($modx->newObject('modUserProfile'));
                }
            }
            break;
    
        //Register this event for authentication in all other contexts
        case "OnWebAuthentication":
        //Register this event for manager authentication only
        case "OnManagerAuthentication":
            $authenticated = false;
            if (isset($user) && !empty($user) && isset($password) && !empty($password)) {
                $crowdAttributes = array (
                    'url' => isset($crowdUrl) ? $crowdUrl : $modx->config['user.crowd.url'],
                    'application' => isset($crowdApp) ? $crowdApp : $modx->config['user.crowd.application'],
                    'credential' => isset($crowdPwd) ? $crowdPwd : $modx->config['user.crowd.credential']
                );
                $modx->addPackage('modx.user.crowd', MODX_CORE_PATH . 'model/');
                if ($crowd = $modx->getService('crowd', 'modCrowdClient', '', $crowdAttributes)) {
                    if ($authenticated = $crowd->authenticate($user->get('username'), $password)) {
                        if ($user->isNew() && is_a($user, 'modCrowdUser') && $modx->config['user.crowd.autoadd']) {
                            if ($userDetails = $crowd->getUser($user->get('username'))) {
                                $user->modUserProfile->set('fullname', implode(" ", array($userDetails['givenName'], $userDetails['sn'])));
                                $user->modUserProfile->set('email', $userDetails['mail']);
                                $user->modUserProfile->set('failed_logins', $userDetails['invalidPasswordAttempts']);
                                $user->modUserProfile->set('last_login', $userDetails['lastAuthenticated']);
                                $authenticated = $user->save();
                            }
                        }
                    }
                }
            }
            $modx->event->_output = $authenticated;
            break;
    }
    ?>
      • 3749
      • 24,544 Posts
      Ok. I have lots of questions but I hope I’m beginning to understand the process.

      I’m still having trouble with this code in the processor login.php:

      if ($loginContext == 'mgr') {
          $rt = $modx->invokeEvent("OnManagerAuthentication", $loginAttributes);
      } else {
          $rt = $modx->invokeEvent("OnWebAuthentication", $loginAttributes);
      }
      // check if plugin authenticated the user
      if (!$rt || (is_array($rt) && !in_array(true, $rt))) {
          // check user password - local authentication
          if($user->password != md5($givenPassword)) {
              $error->failure($modx->lexicon('login_username_password_incorrect'));
          }
      }


      It appears that if the plugin fails the user (say for a bad captcha entry), the login will still succeed here. Is the plugin expected to short-circuit the login process and, if so, what’s the accepted way of doing that? I don’t see it in your example.
        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
        • 22303 MODX Staff
        • 10,725 Posts
        That is the authentication process (i.e. checking actual user credentials); if all the plugins registered for authentication fail, it just tries a simple local password check. I would be inclined to check the captcha in OnBeforeManagerLogin or OnBeforeWebLogin. We may want to do something like check the return value from these events and throw an error message set in the plugin to stop the process appropriately before authentication.
          • 28042 ☆ A M B ☆
          • 24,524 Posts
          Something like the "return true" you need in eForm’s event functions for eForm to continue processing?
            Studying MODX in the desert - http://sottwell.com
            Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
            Join the Slack Community - http://modx.org
            • 22303 MODX Staff
            • 10,725 Posts
            Quote from: sottwell at Aug 11, 2008, 10:38 AM

            Something like the "return true" you need in eForm’s event functions for eForm to continue processing?
            Correct; probably either return true or an error message string that can be displayed to the user. So in login.php, if OnBefore*Login returns true, keep going, otherwise $error->failure($returnedMessage)...
              • 3749
              • 24,544 Posts
              Won’t if($rt) be "true" even if an error message is returned?

              What about turning it around and having the plugins return false if everything is ok and an error message (true) on any failure?

              And shouldn’t onManagerAuthentication work the same way?

              if ($authFailure = $modx->invokeEvent("OnManagerAuthentication", $loginAttributes)) {
               $error->failure($authFailure);
              
              };


                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
                • 22303 MODX Staff
                • 10,725 Posts
                No, a boolean result of true indicates that the execution of the plugin explicitly succeeded. Something like this would work:
                <?php
                if ($rt !== true) {
                  if (!is_string($rt) || empty($rt)) {
                     $rt = 'Error encountered while logging in'; // this would be a lexicon string for default failure message
                  }
                  $error->failure($rt);
                }
                ?>


                The authentication event does work that way, except that if all plugin authentication attempts fail (return boolean false), it must attempt local authentication using the local MODx password. With the remote authentication plugins like the Crowd example, there would be no password in the local table.

                We effectively just have to move the captcha code into a plugin registered to the OnBefore*Login events:
                <?php
                if (isset ($modx->config['use_captcha']) && $modx->config['use_captcha'] == 1) {
                    if (!isset ($_SESSION['veriword']) || $_SESSION['veriword'] != $captcha_code) {
                        $modx->event->_output = $modx->lexicon('login_captcha_error');
                        break;
                    }
                }
                $modx->event->_output = true;
                break;
                ?>

                The only challenge is to determine if we need to explicitly pass in the captcha_code $_POST variable, or simply use the $_POST variable directly (I think the latter is sufficient).
                  • 3749
                  • 24,544 Posts
                  I meant in the original code
                  if (!$rt || (is_array($rt) && !in_array(true, $rt)))
                  where a returned string would definitely be evaluated as true.

                  Your solution is much better than mine, though.

                  Two questions:

                  1. What’s your idea for getting the captcha field into the form?

                  We could put some of these between the existing fields in login.tpl:

                  {$onManagerLoginFormExtraField1}
                  {$onManagerLoginFormExtraField2}


                  2. Does it make sense for the onManagerLoginFormPrerender event to check the use_captcha setting and conditionally register the OnBeforeManagerLogin event or is there a better way?


                    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
                    • 22303 MODX Staff
                    • 10,725 Posts
                    Quote from: BobRay at Aug 11, 2008, 01:45 PM

                    1. What’s your idea for getting the captcha field into the form?

                    We could put some of these between the existing fields in login.tpl:

                    {$onManagerLoginFormExtraField1}
                    {$onManagerLoginFormExtraField2}

                    Let me think about this some more; this could get really ugly if we don’t consider the options here...

                    Quote from: BobRay at Aug 11, 2008, 01:45 PM

                    2. Does it make sense for the onManagerLoginFormPrerender event to check the use_captcha setting and conditionally register the OnBeforeManagerLogin event or is there a better way?
                    I think the plugin should check the use_captcha setting, not the event. There is no such thing as conditionally registering plugins to an event, at least not yet...
                      • 3749
                      • 24,544 Posts
                      Hmmmm... I might be going crazy here.

                      I’ve put this code in the login.php processer:

                      if ($loginContext == 'mgr' || $loginContext == 'connector') {
                             $rt = $modx->invokeEvent("OnBeforeManagerLogin", $onBeforeLoginParams);
                          //$rt = true;
                      } else {
                          $modx->invokeEvent("OnBeforeWebLogin", $onBeforeLoginParams);
                      }
                      
                      //$rt = true;
                      
                      if ($rt !== true) {
                        if (!is_string($rt) || empty($rt)) {
                           $rt = 'Error encountered while logging in'; // this would be a lexicon string for default failure message
                        }
                        $error->failure($rt);
                      }


                      My plugin (OnBeforeManagerLogin) is enabled and the system event is enabled. I know the plugin is executing because if it contains a syntax error, I get "error on page."

                      The plugin has just this line:

                      $modx->event->_output = true;


                      But the login always fails with "Error encountered while logging in". Returning a string in the plugin doesn’t change that.

                      If I uncomment either one of the $rt = true lines in login.php, the login succeeds.

                      What am I missing here?





                        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