<?php __HALT_COMPILER(); ?>
G  	               
   plugin.php  a  3N      3   lib/Sonata/GoogleAuthenticator/FixedBitNotation.php&  a&  _"      6   lib/Sonata/GoogleAuthenticator/GoogleAuthenticator.phpo  ao  ^?'      ?   lib/Sonata/GoogleAuthenticator/GoogleAuthenticatorInterface.php  a  =      .   lib/Sonata/GoogleAuthenticator/GoogleQrUrl.php  a  /|      3   lib/Sonata/GoogleAuthenticator/RuntimeException.php  a        
   config.phpT  aT  Qy         class.auth2fa.phpg  ag  wr)         auth2fa.php&  a&  *˴      <?php

return array(
    'id' =>             '2fa:auth', # notrans
    'version' =>        '0.3',
    'name' =>           /* trans */ 'Two Factor Authenticator',
    'author' =>         'Adriane Alexander',
    'description' =>    /* trans */ 'Provides 2 Factor Authentication
                        using an Authenticator App',
    'url' =>            'https://www.osticket.com/download',
    'plugin' =>         'auth2fa.php:Auth2FAPlugin',
    'requires' => array(
        "sonata-project/google-authenticator" => array(
            "version" => "*",
            "map" => array(
                "sonata-project/google-authenticator/src" => 'lib/Sonata/GoogleAuthenticator',
            )
        ),
    ),
);
?>
<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\GoogleAuthenticator;

/**
 * FixedBitNotation.
 *
 * The FixedBitNotation class is for binary to text conversion. It
 * can handle many encoding schemes, formally defined or not, that
 * use a fixed number of bits to encode each character.
 *
 * @author Andre DeMarre
 */
final class FixedBitNotation
{
    /**
     * @var string
     */
    private $chars;

    /**
     * @var int
     */
    private $bitsPerCharacter;

    /**
     * @var int
     */
    private $radix;

    /**
     * @var bool
     */
    private $rightPadFinalBits;

    /**
     * @var bool
     */
    private $padFinalGroup;

    /**
     * @var string
     */
    private $padCharacter;

    /**
     * @var string[]
     */
    private $charmap;

    /**
     * @param int    $bitsPerCharacter  Bits to use for each encoded character
     * @param string $chars             Base character alphabet
     * @param bool   $rightPadFinalBits How to encode last character
     * @param bool   $padFinalGroup     Add padding to end of encoded output
     * @param string $padCharacter      Character to use for padding
     */
    public function __construct(int $bitsPerCharacter, ?string $chars = null, bool $rightPadFinalBits = false, bool $padFinalGroup = false, string $padCharacter = '=')
    {
        // Ensure validity of $chars
        if (!\is_string($chars) || ($charLength = \strlen($chars)) < 2) {
            $chars =
            '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,';
            $charLength = 64;
        }

        // Ensure validity of $bitsPerCharacter
        if ($bitsPerCharacter < 1) {
            // $bitsPerCharacter must be at least 1
            $bitsPerCharacter = 1;
            $radix = 2;
        } elseif ($charLength < 1 << $bitsPerCharacter) {
            // Character length of $chars is too small for $bitsPerCharacter
            // Set $bitsPerCharacter to greatest acceptable value
            $bitsPerCharacter = 1;
            $radix = 2;

            while ($charLength >= ($radix <<= 1) && $bitsPerCharacter < 8) {
                ++$bitsPerCharacter;
            }

            $radix >>= 1;
        } elseif ($bitsPerCharacter > 8) {
            // $bitsPerCharacter must not be greater than 8
            $bitsPerCharacter = 8;
            $radix = 256;
        } else {
            $radix = 1 << $bitsPerCharacter;
        }

        $this->chars = $chars;
        $this->bitsPerCharacter = $bitsPerCharacter;
        $this->radix = $radix;
        $this->rightPadFinalBits = $rightPadFinalBits;
        $this->padFinalGroup = $padFinalGroup;
        $this->padCharacter = $padCharacter[0];
    }

    /**
     * Encode a string.
     *
     * @param string $rawString Binary data to encode
     */
    public function encode($rawString): string
    {
        // Unpack string into an array of bytes
        $bytes = unpack('C*', $rawString);
        $byteCount = \count($bytes);

        $encodedString = '';
        $byte = array_shift($bytes);
        $bitsRead = 0;

        $chars = $this->chars;
        $bitsPerCharacter = $this->bitsPerCharacter;
        $rightPadFinalBits = $this->rightPadFinalBits;
        $padFinalGroup = $this->padFinalGroup;
        $padCharacter = $this->padCharacter;

        // Generate encoded output;
        // each loop produces one encoded character
        for ($c = 0; $c < $byteCount * 8 / $bitsPerCharacter; ++$c) {
            // Get the bits needed for this encoded character
            if ($bitsRead + $bitsPerCharacter > 8) {
                // Not enough bits remain in this byte for the current
                // character
                // Save the remaining bits before getting the next byte
                $oldBitCount = 8 - $bitsRead;
                $oldBits = $byte ^ ($byte >> $oldBitCount << $oldBitCount);
                $newBitCount = $bitsPerCharacter - $oldBitCount;

                if (!$bytes) {
                    // Last bits; match final character and exit loop
                    if ($rightPadFinalBits) {
                        $oldBits <<= $newBitCount;
                    }
                    $encodedString .= $chars[$oldBits];

                    if ($padFinalGroup) {
                        // Array of the lowest common multiples of
                        // $bitsPerCharacter and 8, divided by 8
                        $lcmMap = [1 => 1, 2 => 1, 3 => 3, 4 => 1, 5 => 5, 6 => 3, 7 => 7, 8 => 1];
                        $bytesPerGroup = $lcmMap[$bitsPerCharacter];
                        $pads = (int) ($bytesPerGroup * 8 / $bitsPerCharacter
                        - ceil((\strlen($rawString) % $bytesPerGroup)
                        * 8 / $bitsPerCharacter));
                        $encodedString .= str_repeat($padCharacter[0], $pads);
                    }

                    break;
                }

                // Get next byte
                $byte = array_shift($bytes);
                $bitsRead = 0;
            } else {
                $oldBitCount = 0;
                $newBitCount = $bitsPerCharacter;
            }

            // Read only the needed bits from this byte
            $bits = $byte >> 8 - ($bitsRead + $newBitCount);
            $bits ^= $bits >> $newBitCount << $newBitCount;
            $bitsRead += $newBitCount;

            if ($oldBitCount) {
                // Bits come from seperate bytes, add $oldBits to $bits
                $bits = ($oldBits << $newBitCount) | $bits;
            }

            $encodedString .= $chars[$bits];
        }

        return $encodedString;
    }

    /**
     * Decode a string.
     *
     * @param string $encodedString Data to decode
     * @param bool   $caseSensitive
     * @param bool   $strict        Returns null if $encodedString contains
     *                              an undecodable character
     */
    public function decode($encodedString, $caseSensitive = true, $strict = false): string
    {
        if (!$encodedString || !\is_string($encodedString)) {
            // Empty string, nothing to decode
            return '';
        }

        $chars = $this->chars;
        $bitsPerCharacter = $this->bitsPerCharacter;
        $radix = $this->radix;
        $rightPadFinalBits = $this->rightPadFinalBits;
        $padCharacter = $this->padCharacter;

        // Get index of encoded characters
        if ($this->charmap) {
            $charmap = $this->charmap;
        } else {
            $charmap = [];

            for ($i = 0; $i < $radix; ++$i) {
                $charmap[$chars[$i]] = $i;
            }

            $this->charmap = $charmap;
        }

        // The last encoded character is $encodedString[$lastNotatedIndex]
        $lastNotatedIndex = \strlen($encodedString) - 1;

        // Remove trailing padding characters
        while ($encodedString[$lastNotatedIndex] === $padCharacter[0]) {
            $encodedString = substr($encodedString, 0, $lastNotatedIndex);
            --$lastNotatedIndex;
        }

        $rawString = '';
        $byte = 0;
        $bitsWritten = 0;

        // Convert each encoded character to a series of unencoded bits
        for ($c = 0; $c <= $lastNotatedIndex; ++$c) {
            if (!isset($charmap[$encodedString[$c]]) && !$caseSensitive) {
                // Encoded character was not found; try other case
                if (isset($charmap[$cUpper = strtoupper($encodedString[$c])])) {
                    $charmap[$encodedString[$c]] = $charmap[$cUpper];
                } elseif (isset($charmap[$cLower = strtolower($encodedString[$c])])) {
                    $charmap[$encodedString[$c]] = $charmap[$cLower];
                }
            }

            if (isset($charmap[$encodedString[$c]])) {
                $bitsNeeded = 8 - $bitsWritten;
                $unusedBitCount = $bitsPerCharacter - $bitsNeeded;

                // Get the new bits ready
                if ($bitsNeeded > $bitsPerCharacter) {
                    // New bits aren't enough to complete a byte; shift them
                    // left into position
                    $newBits = $charmap[$encodedString[$c]] << $bitsNeeded
                    - $bitsPerCharacter;
                    $bitsWritten += $bitsPerCharacter;
                } elseif ($c !== $lastNotatedIndex || $rightPadFinalBits) {
                    // Zero or more too many bits to complete a byte;
                    // shift right
                    $newBits = $charmap[$encodedString[$c]] >> $unusedBitCount;
                    $bitsWritten = 8; //$bitsWritten += $bitsNeeded;
                } else {
                    // Final bits don't need to be shifted
                    $newBits = $charmap[$encodedString[$c]];
                    $bitsWritten = 8;
                }

                $byte |= $newBits;

                if (8 === $bitsWritten || $c === $lastNotatedIndex) {
                    // Byte is ready to be written
                    $rawString .= pack('C', $byte);

                    if ($c !== $lastNotatedIndex) {
                        // Start the next byte
                        $bitsWritten = $unusedBitCount;
                        $byte = ($charmap[$encodedString[$c]]
                        ^ ($newBits << $unusedBitCount)) << 8 - $bitsWritten;
                    }
                }
            } elseif ($strict) {
                // Unable to decode character; abort
                return null;
            }
        }

        return $rawString;
    }
}

// NEXT_MAJOR: Remove class alias
class_alias('Sonata\GoogleAuthenticator\FixedBitNotation', 'Google\Authenticator\FixedBitNotation', false);
<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\GoogleAuthenticator;

/**
 * @see https://github.com/google/google-authenticator/wiki/Key-Uri-Format
 */
final class GoogleAuthenticator implements GoogleAuthenticatorInterface
{
    /**
     * @var int
     */
    private $passCodeLength;

    /**
     * @var int
     */
    private $secretLength;

    /**
     * @var int
     */
    private $pinModulo;

    /**
     * @var \DateTimeInterface
     */
    private $instanceTime;

    /**
     * @var int
     */
    private $codePeriod;

    /**
     * @var int
     */
    private $periodSize = 30;

    public function __construct(int $passCodeLength = 6, int $secretLength = 10, ?\DateTimeInterface $instanceTime = null, int $codePeriod = 30)
    {
        /*
         * codePeriod is the duration in seconds that the code is valid.
         * periodSize is the length of a period to calculate periods since Unix epoch.
         * periodSize cannot be larger than the codePeriod.
         */

        $this->passCodeLength = $passCodeLength;
        $this->secretLength = $secretLength;
        $this->codePeriod = $codePeriod;
        $this->periodSize = $codePeriod < $this->periodSize ? $codePeriod : $this->periodSize;
        $this->pinModulo = 10 ** $passCodeLength;
        $this->instanceTime = $instanceTime ?? new \DateTimeImmutable();
    }

    /**
     * @param string $secret
     * @param string $code
     * @param int    $discrepancy
     */
    public function checkCode($secret, $code, $discrepancy = 1): bool
    {
        /**
         * Discrepancy is the factor of periodSize ($discrepancy * $periodSize) allowed on either side of the
         * given codePeriod. For example, if a code with codePeriod = 60 is generated at 10:00:00, a discrepancy
         * of 1 will allow a periodSize of 30 seconds on either side of the codePeriod resulting in a valid code
         * from 09:59:30 to 10:00:29.
         *
         * The result of each comparison is stored as a timestamp here instead of using a guard clause
         * (https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html). This is to implement
         * constant time comparison to make side-channel attacks harder. See
         * https://cryptocoding.net/index.php/Coding_rules#Compare_secret_strings_in_constant_time for details.
         * Each comparison uses hash_equals() instead of an operator to implement constant time equality comparison
         * for each code.
         */
        $periods = floor($this->codePeriod / $this->periodSize);

        $result = 0;
        for ($i = -$discrepancy; $i < $periods + $discrepancy; ++$i) {
            $dateTime = new \DateTimeImmutable('@'.($this->instanceTime->getTimestamp() - ($i * $this->periodSize)));
            $result = hash_equals($this->getCode($secret, $dateTime), $code) ? $dateTime->getTimestamp() : $result;
        }

        return $result > 0;
    }

    /**
     * NEXT_MAJOR: add the interface typehint to $time and remove deprecation.
     *
     * @param string                                   $secret
     * @param float|string|int|\DateTimeInterface|null $time
     */
    public function getCode($secret, /* \DateTimeInterface */ $time = null): string
    {
        if (null === $time) {
            $time = $this->instanceTime;
        }

        if ($time instanceof \DateTimeInterface) {
            $timeForCode = floor($time->getTimestamp() / $this->periodSize);
        } else {
            @trigger_error(
                'Passing anything other than null or a DateTimeInterface to $time is deprecated as of 2.0 '.
                'and will not be possible as of 3.0.',
                \E_USER_DEPRECATED
            );
            $timeForCode = $time;
        }

        $base32 = new FixedBitNotation(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', true, true);
        $secret = $base32->decode($secret);

        $timeForCode = str_pad(pack('N', $timeForCode), 8, \chr(0), \STR_PAD_LEFT);

        $hash = hash_hmac('sha1', $timeForCode, $secret, true);
        $offset = \ord(substr($hash, -1));
        $offset &= 0xF;

        $truncatedHash = $this->hashToInt($hash, $offset) & 0x7FFFFFFF;

        return str_pad((string) ($truncatedHash % $this->pinModulo), $this->passCodeLength, '0', \STR_PAD_LEFT);
    }

    /**
     * NEXT_MAJOR: Remove this method.
     *
     * @param string $user
     * @param string $hostname
     * @param string $secret
     *
     * @deprecated deprecated as of 2.1 and will be removed in 3.0. Use Sonata\GoogleAuthenticator\GoogleQrUrl::generate() instead.
     */
    public function getUrl($user, $hostname, $secret): string
    {
        @trigger_error(sprintf(
            'Using %s() is deprecated as of 2.1 and will be removed in 3.0. '.
            'Use Sonata\GoogleAuthenticator\GoogleQrUrl::generate() instead.',
            __METHOD__
        ), \E_USER_DEPRECATED);

        $issuer = \func_get_args()[3] ?? null;
        $accountName = sprintf('%s@%s', $user, $hostname);

        // manually concat the issuer to avoid a change in URL
        $url = GoogleQrUrl::generate($accountName, $secret);

        if ($issuer) {
            $url .= '%26issuer%3D'.$issuer;
        }

        return $url;
    }

    public function generateSecret(): string
    {
        return (new FixedBitNotation(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', true, true))
            ->encode(random_bytes($this->secretLength));
    }

    private function hashToInt(string $bytes, int $start): int
    {
        return unpack('N', substr(substr($bytes, $start), 0, 4))[1];
    }
}

// NEXT_MAJOR: Remove class alias
class_alias('Sonata\GoogleAuthenticator\GoogleAuthenticator', 'Google\Authenticator\GoogleAuthenticator', false);
<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\GoogleAuthenticator;

interface GoogleAuthenticatorInterface
{
    /**
     * @param string $secret
     * @param string $code
     */
    public function checkCode($secret, $code, $discrepancy = 1): bool;

    /**
     * NEXT_MAJOR: add the interface typehint to $time and remove deprecation.
     *
     * @param string                                   $secret
     * @param float|string|int|\DateTimeInterface|null $time
     */
    public function getCode($secret, /* \DateTimeInterface */ $time = null): string;

    /**
     * NEXT_MAJOR: Remove this method.
     *
     * @param string $user
     * @param string $hostname
     * @param string $secret
     *
     * @deprecated deprecated as of 2.1 and will be removed in 3.0. Use Sonata\GoogleAuthenticator\GoogleQrUrl::generate() instead.
     */
    public function getUrl($user, $hostname, $secret): string;

    public function generateSecret(): string;
}
<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\GoogleAuthenticator;

/**
 * Responsible for QR image url generation.
 *
 * @see http://goqr.me/api/
 * @see http://goqr.me/api/doc/
 * @see https://github.com/google/google-authenticator/wiki/Key-Uri-Format
 *
 * @author Iltar van der Berg <kjarli@gmail.com>
 */
final class GoogleQrUrl
{
    /**
     * Private by design.
     */
    private function __construct()
    {
    }

    /**
     * Generates a URL that is used to show a QR code.
     *
     * Account names may not contain a double colon (:). Valid account name
     * examples:
     *  - "John.Doe@gmail.com"
     *  - "John Doe"
     *  - "John_Doe_976"
     *
     * The Issuer may not contain a double colon (:). The issuer is recommended
     * to pass along. If used, it will also be appended before the accountName.
     *
     * The previous examples with the issuer "Acme inc" would result in label:
     *  - "Acme inc:John.Doe@gmail.com"
     *  - "Acme inc:John Doe"
     *  - "Acme inc:John_Doe_976"
     *
     * The contents of the label, issuer and secret will be encoded to generate
     * a valid URL.
     *
     * @param string      $accountName The account name to show and identify
     * @param string      $secret      The secret is the generated secret unique to that user
     * @param string|null $issuer      Where you log in to
     * @param int         $size        Image size in pixels, 200 will make it 200x200
     */
    public static function generate(string $accountName, string $secret, ?string $issuer = null, int $size = 200): string
    {
        if ('' === $accountName || false !== strpos($accountName, ':')) {
            throw RuntimeException::InvalidAccountName($accountName);
        }

        if ('' === $secret) {
            throw RuntimeException::InvalidSecret();
        }

        $label = $accountName;
        $otpauthString = 'otpauth://totp/%s?secret=%s';

        if (null !== $issuer) {
            if ('' === $issuer || false !== strpos($issuer, ':')) {
                throw RuntimeException::InvalidIssuer($issuer);
            }

            // use both the issuer parameter and label prefix as recommended by Google for BC reasons
            $label = $issuer.':'.$label;
            $otpauthString .= '&issuer=%s';
        }

        $otpauthString = rawurlencode(sprintf($otpauthString, $label, $secret, $issuer));

        return sprintf(
            'https://api.qrserver.com/v1/create-qr-code/?size=%1$dx%1$d&data=%2$s&ecc=M',
            $size,
            $otpauthString
        );
    }
}

// NEXT_MAJOR: Remove class alias
class_alias('Sonata\GoogleAuthenticator\GoogleQrUrl', 'Google\Authenticator\GoogleQrUrl', false);
<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\GoogleAuthenticator;

/**
 * Contains runtime exception templates.
 *
 * @author Iltar van der Berg <kjarli@gmail.com>
 */
final class RuntimeException extends \RuntimeException
{
    public static function InvalidAccountName(string $accountName): self
    {
        return new self(sprintf(
            'The account name may not contain a double colon (:) and may not be an empty string. Given "%s".',
            $accountName
        ));
    }

    public static function InvalidIssuer(string $issuer): self
    {
        return new self(sprintf(
            'The issuer name may not contain a double colon (:) and may not be an empty string. Given "%s".',
            $issuer
        ));
    }

    public static function InvalidSecret(): self
    {
        return new self('The secret name may not be an empty string.');
    }
}

// NEXT_MAJOR: Remove class alias
class_alias('Sonata\GoogleAuthenticator\RuntimeException', 'Google\Authenticator\RuntimeException', false);
<?php

require_once INCLUDE_DIR . 'class.plugin.php';
require_once INCLUDE_DIR.'class.forms.php';

class Auth2FAConfig extends PluginConfig {

    // Provide compatibility function for versions of osTicket prior to
    // translation support (v1.9.4)
    function translate() {
        if (!method_exists('Plugin', 'translate')) {
            return array(
                function($x) { return $x; },
                function($x, $y, $n) { return $n != 1 ? $y : $x; },
            );
        }
        return Plugin::translate('2fa-auth');
    }

    function getOptions() {
        return array(
            'custom_issuer' => new TextboxField(array(
                'label' => __('Issuer'),
                'required' => false,
                'configuration' => array('size'=>40),
                'hint' => __('Customize the Issuer shown in your Authenticator app after scanning a QR Code.'),
            )),
        );
    }

    function pre_save(&$config, &$errors) {
        global $msg;
        if (!$errors)
            $msg = __('Configuration updated successfully');
        return true;
    }
}
<?php
require_once INCLUDE_DIR . 'class.export.php';

class Auth2FABackend extends TwoFactorAuthenticationBackend {
    static $id = "auth.agent";
    static $name = "Authenticator";

    static $desc = /* @trans */ 'Verification codes are located in the Authenticator app of your choice on your phone';
    static $custom_issuer;

    var $secretKey;

    protected function getSetupOptions() {
        global $thisstaff;

        $auth2FA = new Auth2FABackend;
        $qrCodeURL = $auth2FA->getQRCode($thisstaff);
        if ($auth2FA->validateQRCode($thisstaff)) {
            return array(
                '' => new FreeTextField(array(
                    'configuration' => array(
                        'content' => sprintf(
                            '<input type="hidden" name="email" value="%s" />
                            <em>Use an Authenticator application on your phone to scan
                                the QR Code below. If you lose the QR Code
                                on the app, you will need to have your 2FA configurations reset by
                                a helpdesk Administrator.</em>
                            </br>
                            <tr>
                                <td>
                                <img src="%s" alt="QR Code" />
                                </td>
                            </tr>',
                            $thisstaff->getEmail(), $qrCodeURL),
                    )
                )),
            );
        }
    }

    protected function getInputOptions() {
        return array(
            'token' => new TextboxField(array(
                'id'=>1, 'label'=>__('Verification Code'), 'required'=>true, 'default'=>'',
                'validator'=>'number',
                'hint'=>__('Please enter the code from your Authenticator app'),
                'configuration'=>array(
                    'size'=>40, 'length'=>40,
                    'autocomplete' => 'one-time-code',
                    'inputmode' => 'numeric',
                    'pattern' => '[0-9]*',
                    'validator-error' => __('Invalid Code format'),
                    ),
            )),
        );
    }

    function validate($form, $user) {
        // Make sure form is valid and token exists
        if (!($form->isValid()
                    && ($clean=$form->getClean())
                    && $clean['token']))
            return false;

        if (!$this->validateLoginCode($clean['token']))
            return false;

        // upstream validation might throw an exception due to expired token
        // or too many attempts (timeout). It's the responsibility of the
        // caller to catch and handle such exceptions.
        $secretKey = $this->getSecretKey();
        if (!$this->_validate($secretKey))
            return false;

        // Validator doesn't do house cleaning - it's our responsibility
        $this->onValidate($user);

        return true;
    }

    function send($user) {
        global $cfg;

        // Get backend configuration for this user
        if (!$cfg || !($info = $user->get2FAConfig($this->getId())))
            return false;

        // get configuration
        $config = $info['config'];

        // Generate Secret Key
        if (!$this->secretKey)
            $this->secretKey = $this->getSecretKey($user);

        $this->store($this->secretKey);

        return true;
    }

    function store($secretKey) {
       global $thisstaff;

       $store =  &$_SESSION['_2fa'][$this->getId()];
       $store = ['otp' => $secretKey, 'time' => time(), 'strikes' => 0];

       if ($thisstaff) {
           $config = array('config' => array('key' => $secretKey, 'external2fa' => true));
           $_config = new Config('staff.'.$thisstaff->getId());
           $_config->set($this->getId(), JsonDataEncoder::encode($config));
           $thisstaff->_config = $_config->getInfo();
           $errors['err'] = '';
       }

       return $store;
    }

    function validateLoginCode($code) {
        $auth2FA = new \Sonata\GoogleAuthenticator\GoogleAuthenticator();
        $secretKey = $this->getSecretKey();

        return $auth2FA->checkCode($secretKey, $code);
    }

    function getSecretKey($staff=false) {
        if (!$staff) {
            $s = StaffAuthenticationBackend::getUser();
            $staff = Staff::lookup($s->getId());
        }

        if (!$token = ConfigItem::getConfigsByNamespace('staff.'.$staff->getId(), static::$id)) {
            $auth2FA = new \Sonata\GoogleAuthenticator\GoogleAuthenticator();
            $this->secretKey = $auth2FA->generateSecret();
            $this->store($this->secretKey);
        }

        $key = $token->value ?: $this->secretKey;
        if (strpos($key, 'config')) {
            $key = json_decode($key, true);
            $key = $key['config']['key'];
        }

        return $key;
    }

    function getQRCode($staff=false) {
        $staffEmail = $staff->getEmail();
        $secretKey = $this->getSecretKey($staff);
        $title = preg_replace('/[^A-Za-z0-9]/', '', self::$custom_issuer ?: __('osTicket'));

        return \Sonata\GoogleAuthenticator\GoogleQrUrl::generate($staffEmail, $secretKey, $title);
    }

    function validateQRCode($staff=false) {
        $auth2FA = new \Sonata\GoogleAuthenticator\GoogleAuthenticator();
        $secretKey = $this->getSecretKey($staff);
        $code = self::getCode();

        return $auth2FA->checkCode($secretKey, $code);
    }

    static function getCode() {
        $auth2FA = new \Sonata\GoogleAuthenticator\GoogleAuthenticator();
        $self = new Auth2FABackend();
        $secretKey = $self->getSecretKey();

        return $auth2FA->getCode($secretKey);
    }
}
<?php

require_once(INCLUDE_DIR.'class.plugin.php');
require_once('config.php');
require_once('class.auth2fa.php');

class Auth2FAPlugin extends Plugin {
    var $config_class = "Auth2FAConfig";

    function bootstrap() {
        $config = $this->getConfig();
        if ($config->get('custom_issuer'))
            Auth2FABackend::$custom_issuer = $config->get('custom_issuer');

        TwoFactorAuthenticationBackend::register('Auth2FABackend');
    }

    function enable() {
        return parent::enable();
    }

    function uninstall(&$errors) {
        $errors = array();

        self::disable();

        return parent::uninstall($errors);
    }

    function disable() {
        $default2fas = ConfigItem::getConfigsByNamespace(false, 'default_2fa', Auth2FABackend::$id);
        foreach($default2fas as $default2fa)
            $default2fa->delete();

        $tokens = ConfigItem::getConfigsByNamespace(false, Auth2FABackend::$id);
        foreach($tokens as $token)
            $token->delete();

        return parent::disable();
    }
}

require_once(INCLUDE_DIR.'UniversalClassLoader.php');
use Symfony\Component\ClassLoader\UniversalClassLoader_osTicket;
$loader = new UniversalClassLoader_osTicket();
$loader->registerNamespaceFallbacks(array(
    dirname(__file__).'/lib'));
$loader->register();
yfP-º%gքFX@UD   GBMB