Skip to content
Snippets Groups Projects
class.auth.php 41.1 KiB
Newer Older
  • Learn to ignore specific revisions
  •         if (!isset($_POST['userid']) || !isset($_POST['token']))
                return false;
            elseif (!($_config = new Config('pwreset')))
                return false;
    
    Jared Hancock's avatar
    Jared Hancock committed
            elseif (($staff = StaffSession::lookup($_POST['userid'])) &&
    
                    !$staff->getId())
    
                $errors['msg'] = __('Invalid user-id given');
    
            elseif (!($id = $_config->get($_POST['token']))
                    || $id != $staff->getId())
    
                $errors['msg'] = __('Invalid reset token');
    
            elseif (!($ts = $_config->lastModified($_POST['token']))
                    && ($ost->getConfig()->getPwResetWindow() < (time() - strtotime($ts))))
    
                $errors['msg'] = __('Invalid reset token');
    
            elseif (!$staff->forcePasswdRest())
    
                $errors['msg'] = __('Unable to reset password');
    
            else
                return $staff;
        }
    
        function login($staff, $bk) {
            $_SESSION['_staff']['reset-token'] = $_POST['token'];
            Signal::send('auth.pwreset.login', $staff);
            return parent::login($staff, $bk);
        }
    }
    
    StaffAuthenticationBackend::register('PasswordResetTokenBackend');
    
    /*
     * AuthToken Authentication Backend
     *
     * Provides auto-login facility for end users with valid link
     *
     * Ticket used to loggin is tracked durring the session this is
     * important in the future when auto-logins will be
     * limited to single ticket view.
     */
    
    class AuthTokenAuthentication extends UserAuthenticationBackend {
        static $name = "Auth Token Authentication";
        static $id = "authtoken";
    
    
        function signOn() {
    
    
            if ($_GET['auth']) {
                if (($u = TicketUser::lookupByToken($_GET['auth'])))
                    $user = new ClientSession($u);
            }
    
            // Support old ticket based tokens.
            elseif ($_GET['t'] && $_GET['e'] && $_GET['a']) {
    
                if (($ticket = Ticket::lookupByNumber($_GET['t'], $_GET['e']))
    
                        // Using old ticket auth code algo - hardcoded here because it
                        // will be removed in ticket class in the upcoming rewrite
                        && !strcasecmp($_GET['a'], md5($ticket->getId() .  $_GET['e'] . SECRET_SALT))
    
                        && ($owner = $ticket->getOwner()))
                    $user = new ClientSession($owner);
    
        function supportsInteractiveAuthentication() {
            return false;
        }
    
    
        protected function getAuthKey($user) {
    
    
                return null;
    
            //Generate authkey based the type of ticket user
            // It's required to validate users going forward.
            $authkey = sprintf('%s%dt%dh%s',  //XXX: Placeholder
    
                        ($user->isOwner() ? 'o':'c'),
    
                        $user->getId(),
    
                        $user->getTicketId(),
                        md5($user->getId().$this->id));
    
            return $authkey;
    
        protected function validate($authkey) {
    
            $regex = '/^(?P<type>\w{1})(?P<id>\d+)t(?P<tid>\d+)h(?P<hash>.*)$/i';
            $matches = array();
            if (!preg_match($regex, $authkey, $matches))
                return false;
    
            $user = null;
            switch ($matches['type']) {
                case 'c': //Collaborator
    
                    $criteria = array(
                        'user_id' => $matches['id'],
                        'thread__ticket__ticket_id' => $matches['tid']
                    );
    
                    if (($c = Collaborator::lookup($criteria))
    
                            && ($c->getTicketId() == $matches['tid']))
                        $user = new ClientSession($c);
                    break;
                case 'o': //Ticket owner
                    if (($ticket = Ticket::lookup($matches['tid']))
    
                            && ($o = $ticket->getOwner())
                            && ($o->getId() == $matches['id']))
                        $user = new ClientSession($o);
    
            //Make sure the authkey matches.
            if (!$user || strcmp($this->getAuthKey($user), $authkey))
    
            $user->flagGuest();
    
    UserAuthenticationBackend::register('AuthTokenAuthentication');
    
    //Simple ticket lookup backend used to recover ticket access link.
    // We're using authentication backend so we can guard aganist brute force
    // attempts (which doesn't buy much since the link is emailed)
    class AccessLinkAuthentication extends UserAuthenticationBackend {
        static $name = "Ticket Access Link Authentication";
        static $id = "authlink";
    
        function authenticate($email, $number) {
    
            if (!($ticket = Ticket::lookupByNumber($number))
    
                    || !($user=User::lookup(array('emails__address' => $email))))
    
            if (!($user = $this->_getTicketUser($ticket, $user)))
                return false;
    
            $_SESSION['_auth']['user-ticket'] = $number;
            return new ClientSession($user);
        }
    
        function _getTicketUser($ticket, $user) {
    
            // Ticket owner?
    
            if ($ticket->getUserId() == $user->getId())
                $user = $ticket->getOwner();
    
            // Collaborator?
            elseif (!($user = Collaborator::lookup(array(
    
                    'user_id' => $user->getId(),
                    'thread__ticket__ticket_id' => $ticket->getId())
            )))
    
                return false; //Bro, we don't know you!
    
    
        // We are not actually logging in the user....
    
        function login($user, $bk) {
    
            global $cfg;
    
            if (!$cfg->isClientEmailVerificationRequired()) {
                return parent::login($user, $bk);
            }
    
            return true;
        }
    
    
        protected function validate($userid) {
            $number = $_SESSION['_auth']['user-ticket'];
    
            if (!($ticket = Ticket::lookupByNumber($number)))
                return false;
    
            if (!($user = User::lookup($userid)))
                return false;
    
            if (!($user = $this->_getTicketUser($ticket, $user)))
                return false;
    
            $user = new ClientSession($user);
            $user->flagGuest();
            return $user;
        }
    
    
        function supportsInteractiveAuthentication() {
            return false;
        }
    
    }
    UserAuthenticationBackend::register('AccessLinkAuthentication');
    
    
    class osTicketClientAuthentication extends UserAuthenticationBackend {
        static $name = "Local Client Authentication";
        static $id = "client";
    
        function authenticate($username, $password) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            if (!($acct = ClientAccount::lookupByUsername($username)))
    
                return;
    
    Jared Hancock's avatar
    Jared Hancock committed
            if (($client = new ClientSession(new EndUser($acct->getUser())))
    
                    && !$client->getId())
                return false;
            elseif (!$acct->checkPassword($password))
                return false;
            else
    
                return $client;
        }
    }
    UserAuthenticationBackend::register('osTicketClientAuthentication');
    
    class ClientPasswordResetTokenBackend extends UserAuthenticationBackend {
        static $id = "pwreset.client";
    
    
        function supportsInteractiveAuthentication() {
    
            return false;
        }
    
        function signOn($errors=array()) {
            global $ost;
    
            if (!isset($_POST['userid']) || !isset($_POST['token']))
                return false;
            elseif (!($_config = new Config('pwreset')))
                return false;
            elseif (!($acct = ClientAccount::lookupByUsername($_POST['userid']))
                    || !$acct->getId()
                    || !($client = new ClientSession(new EndUser($acct->getUser()))))
    
                $errors['msg'] = __('Invalid user-id given');
    
            elseif (!($id = $_config->get($_POST['token']))
    
                    || $id != 'c'.$client->getId())
    
                $errors['msg'] = __('Invalid reset token');
    
            elseif (!($ts = $_config->lastModified($_POST['token']))
                    && ($ost->getConfig()->getPwResetWindow() < (time() - strtotime($ts))))
    
                $errors['msg'] = __('Invalid reset token');
    
            elseif (!$acct->forcePasswdReset())
    
                $errors['msg'] = __('Unable to reset password');
    
                return $client;
    
        function login($client, $bk) {
            $_SESSION['_client']['reset-token'] = $_POST['token'];
            Signal::send('auth.pwreset.login', $client);
            return parent::login($client, $bk);
        }
    
    UserAuthenticationBackend::register('ClientPasswordResetTokenBackend');
    
    
    class ClientAcctConfirmationTokenBackend extends UserAuthenticationBackend {
        static $id = "confirm.client";
    
    
        function supportsInteractiveAuthentication() {
    
            return false;
        }
    
        function signOn($errors=array()) {
            global $ost;
    
            if (!isset($_GET['token']))
                return false;
            elseif (!($_config = new Config('pwreset')))
                return false;
            elseif (!($id = $_config->get($_GET['token'])))
                return false;
    
            elseif (!($acct = ClientAccount::lookup(array('user_id'=>substr($id,1))))
    
                    || !$acct->getId()
    
                    || $id != 'c'.$acct->getUserId()
    
                    || !($client = new ClientSession(new EndUser($acct->getUser()))))
                return false;
            else
                return $client;
        }
    }
    UserAuthenticationBackend::register('ClientAcctConfirmationTokenBackend');
    
    
    // ----- Password Policy --------------------------------------
    
    class BadPassword extends Exception {}
    class PasswordUpdateFailed extends Exception {}
    
    abstract class PasswordPolicy {
        static protected $registry = array();
    
        /**
         * Check a password and throw BadPassword with a meaningful message if
         * the password cannot be accepted.
         */
        abstract function processPassword($new, $current);
    
        static function checkPassword($new, $current) {
            foreach (static::allActivePolicies() as $P) {
                $P->processPassword($new, $current);
            }
        }
    
        static function allActivePolicies() {
            $policies = array();
            foreach (static::$registry as $P) {
                if (is_string($P) && class_exists($P))
                    $P = new $P();
                if ($P instanceof PasswordPolicy)
                    $policies[] = $P;
            }
            return $policies;
        }
    
        static function register($policy) {
            static::$registry[] = $policy;
        }
    }
    
    class osTicketPasswordPolicy
    extends PasswordPolicy {
        function processPassword($passwd, $current) {
            if (strlen($passwd) < 6) {
                throw new BadPassword(
                    __('Password must be at least 6 characters'));
            }
            // XXX: Changing case is technicall changing the password
            if (0 === strcasecmp($passwd, $current)) {
                throw new BadPassword(
                    __('New password MUST be different from the current password!'));
            }
        }
    }
    PasswordPolicy::register('osTicketPasswordPolicy');