summaryrefslogtreecommitdiffstats
path: root/vendor/sonata-project
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/sonata-project')
-rw-r--r--vendor/sonata-project/google-authenticator/LICENSE21
-rw-r--r--vendor/sonata-project/google-authenticator/composer.json50
-rw-r--r--vendor/sonata-project/google-authenticator/sample/example.php41
-rw-r--r--vendor/sonata-project/google-authenticator/sample/tmpl/ask-for-otp.php23
-rw-r--r--vendor/sonata-project/google-authenticator/sample/tmpl/loggedin.php19
-rw-r--r--vendor/sonata-project/google-authenticator/sample/tmpl/login-error.php6
-rw-r--r--vendor/sonata-project/google-authenticator/sample/tmpl/login.php8
-rw-r--r--vendor/sonata-project/google-authenticator/sample/tmpl/show-qr.php11
-rw-r--r--vendor/sonata-project/google-authenticator/sample/users.dat1
-rw-r--r--vendor/sonata-project/google-authenticator/sample/web/Users.php155
-rw-r--r--vendor/sonata-project/google-authenticator/sample/web/index.php119
-rw-r--r--vendor/sonata-project/google-authenticator/src/FixedBitNotation.php292
-rw-r--r--vendor/sonata-project/google-authenticator/src/GoogleAuthenticator.php178
-rw-r--r--vendor/sonata-project/google-authenticator/src/GoogleAuthenticatorInterface.php44
-rw-r--r--vendor/sonata-project/google-authenticator/src/GoogleQrUrl.php93
-rw-r--r--vendor/sonata-project/google-authenticator/src/RuntimeException.php46
16 files changed, 1107 insertions, 0 deletions
diff --git a/vendor/sonata-project/google-authenticator/LICENSE b/vendor/sonata-project/google-authenticator/LICENSE
new file mode 100644
index 0000000..0135e0b
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2010 Thomas Rabaix
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/sonata-project/google-authenticator/composer.json b/vendor/sonata-project/google-authenticator/composer.json
new file mode 100644
index 0000000..4bd395c
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/composer.json
@@ -0,0 +1,50 @@
+{
+ "name": "sonata-project/google-authenticator",
+ "type": "library",
+ "description": "Library to integrate Google Authenticator into a PHP project",
+ "keywords": [
+ "google authenticator"
+ ],
+ "homepage": "https://github.com/sonata-project/GoogleAuthenticator",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Thomas Rabaix",
+ "email": "thomas.rabaix@gmail.com"
+ },
+ {
+ "name": "Christian Stocker",
+ "email": "me@chregu.tv"
+ },
+ {
+ "name": "Andre DeMarre",
+ "homepage": "http://www.devnetwork.net/viewtopic.php?f=50&t=94989"
+ }
+ ],
+ "require": {
+ "php": "^7.3 || ^8.0"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge": "^5.1.8"
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Google\\Authenticator\\": "src/",
+ "Sonata\\GoogleAuthenticator\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Google\\Authenticator\\Tests\\": "tests/",
+ "Sonata\\GoogleAuthenticator\\Tests\\": "tests/"
+ }
+ }
+}
diff --git a/vendor/sonata-project/google-authenticator/sample/example.php b/vendor/sonata-project/google-authenticator/sample/example.php
new file mode 100644
index 0000000..50366d2
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/example.php
@@ -0,0 +1,41 @@
+<?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.
+ */
+
+include_once __DIR__.'/../src/FixedBitNotation.php';
+include_once __DIR__.'/../src/GoogleAuthenticator.php';
+include_once __DIR__.'/../src/GoogleQrUrl.php';
+
+$secret = 'XVQ2UIGO75XRUKJO';
+$code = '846474';
+
+$g = new \Sonata\GoogleAuthenticator\GoogleAuthenticator();
+
+echo 'Current Code is: ';
+echo $g->getCode($secret);
+
+echo "\n";
+
+echo "Check if $code is valid: ";
+
+if ($g->checkCode($secret, $code)) {
+ echo "YES \n";
+} else {
+ echo "NO \n";
+}
+
+$secret = $g->generateSecret();
+echo "Get a new Secret: $secret \n";
+echo "The QR Code for this secret (to scan with the Google Authenticator App: \n";
+
+echo \Sonata\GoogleAuthenticator\GoogleQrUrl::generate('chregu', $secret, 'GoogleAuthenticatorExample');
+echo "\n";
diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/ask-for-otp.php b/vendor/sonata-project/google-authenticator/sample/tmpl/ask-for-otp.php
new file mode 100644
index 0000000..f3e06d4
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/tmpl/ask-for-otp.php
@@ -0,0 +1,23 @@
+
+<h1>please otp</h1>
+<p>
+<form method="post" action="./">
+<?php if ($debug) {
+ ?>
+ <br/>
+ (Set $debug in index.php to false, if you don't want to have the OTP prefilled (for real life application, for example ;))<br/>
+<?php
+}
+?>
+
+otp: <input name="otp"
+value="<?php
+if ($debug) {
+ $g = new GoogleAuthenticator();
+ echo $g->getCode($user->getSecret());
+}
+?>"/><br/>
+<input type="checkbox" name="remember" id="remember" /><label for="remember"> Remember verification for this computer for 1 day.</label> <br/>
+<input type="submit"/>
+
+</form>
diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/loggedin.php b/vendor/sonata-project/google-authenticator/sample/tmpl/loggedin.php
new file mode 100644
index 0000000..2a19032
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/tmpl/loggedin.php
@@ -0,0 +1,19 @@
+
+<p>
+Hello <?php echo $user->getUsername(); ?>
+</p>
+<?php
+if (!isset($_GET['showqr'])) {
+ ?>
+
+<p>
+<a href="?showqr=1">Show QR Code</a>
+</p>
+
+<?php
+}
+?>
+
+<p>
+<a href="?logout=1">Logout</a>
+</p>
diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/login-error.php b/vendor/sonata-project/google-authenticator/sample/tmpl/login-error.php
new file mode 100644
index 0000000..8d23fd3
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/tmpl/login-error.php
@@ -0,0 +1,6 @@
+<p>
+Wrong username or password or token.
+</p>
+<p>
+<a href="./">try again</a>
+</p>
diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/login.php b/vendor/sonata-project/google-authenticator/sample/tmpl/login.php
new file mode 100644
index 0000000..fd81623
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/tmpl/login.php
@@ -0,0 +1,8 @@
+
+<h1>please login</h1>
+<p>
+<form method="post" action="./">
+username: <input name="username"/><br/>
+password: <input name="password" type="password"/><br/>
+<input type="submit"/>
+</form>
diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/show-qr.php b/vendor/sonata-project/google-authenticator/sample/tmpl/show-qr.php
new file mode 100644
index 0000000..774a298
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/tmpl/show-qr.php
@@ -0,0 +1,11 @@
+<h1>Please scan this </h1>
+
+<p> with <a href="http://www.google.com/support/a/bin/answer.py?hl=en&answer=1037451">the Google Authenticator App</a></p>
+
+<p>
+<?php
+$link = \Sonata\GoogleAuthenticator\GoogleQrUrl::generate($user->getUsername(), $secret, 'GoogleAuthenticatorExample');
+?>
+
+<a href="<?php echo $link; ?>"><img style="border: 0; padding:10px" src="<?php echo $link; ?>"/></a>
+</p>
diff --git a/vendor/sonata-project/google-authenticator/sample/users.dat b/vendor/sonata-project/google-authenticator/sample/users.dat
new file mode 100644
index 0000000..fdcc130
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/users.dat
@@ -0,0 +1 @@
+{"chregu":{"password":"foobar"}} \ No newline at end of file
diff --git a/vendor/sonata-project/google-authenticator/sample/web/Users.php b/vendor/sonata-project/google-authenticator/sample/web/Users.php
new file mode 100644
index 0000000..410ed48
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/web/Users.php
@@ -0,0 +1,155 @@
+<?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.
+ */
+
+class Users
+{
+ public function __construct(string $file = '../users.dat')
+ {
+ $this->userFile = $file;
+
+ $this->users = json_decode(file_get_contents($file), true);
+ }
+
+ public function hasSession()
+ {
+ session_start();
+ if (isset($_SESSION['username'])) {
+ return $_SESSION['username'];
+ }
+
+ return false;
+ }
+
+ public function storeData(User $user): void
+ {
+ $this->users[$user->getUsername()] = $user->getData();
+ file_put_contents($this->userFile, json_encode($this->users));
+ }
+
+ public function loadUser($name)
+ {
+ if (isset($this->users[$name])) {
+ return new User($name, $this->users[$name]);
+ }
+
+ return false;
+ }
+}
+
+class User
+{
+ public function __construct($user, $data)
+ {
+ $this->data = $data;
+ $this->user = $user;
+ }
+
+ public function auth($pass)
+ {
+ if ($this->data['password'] === $pass) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public function startSession(): void
+ {
+ $_SESSION['username'] = $this->user;
+ }
+
+ public function doLogin(): void
+ {
+ session_regenerate_id();
+ $_SESSION['loggedin'] = true;
+ $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];
+ }
+
+ public function doOTP(): void
+ {
+ $_SESSION['OTP'] = true;
+ }
+
+ public function isOTP()
+ {
+ if (isset($_SESSION['OTP']) && true === $_SESSION['OTP']) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public function isLoggedIn()
+ {
+ if (isset($_SESSION['loggedin']) && true === $_SESSION['loggedin'] &&
+ isset($_SESSION['ua']) && $_SESSION['ua'] === $_SERVER['HTTP_USER_AGENT']
+ ) {
+ return $_SESSION['username'];
+ }
+
+ return false;
+ }
+
+ public function getUsername()
+ {
+ return $this->user;
+ }
+
+ public function getSecret()
+ {
+ if (isset($this->data['secret'])) {
+ return $this->data['secret'];
+ }
+
+ return false;
+ }
+
+ public function generateSecret()
+ {
+ $g = new \Sonata\GoogleAuthenticator\GoogleAuthenticator();
+ $secret = $g->generateSecret();
+ $this->data['secret'] = $secret;
+
+ return $secret;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function setOTPCookie(): void
+ {
+ $time = floor(time() / (3600 * 24)); // get day number
+ //about using the user agent: It's easy to fake it, but it increases the barrier for stealing and reusing cookies nevertheless
+ // and it doesn't do any harm (except that it's invalid after a browser upgrade, but that may be even intented)
+ $cookie = $time.':'.hash_hmac('sha1', $this->getUsername().':'.$time.':'.$_SERVER['HTTP_USER_AGENT'], $this->getSecret());
+ setcookie('otp', $cookie, time() + (30 * 24 * 3600), null, null, null, true);
+ }
+
+ public function hasValidOTPCookie()
+ {
+ // 0 = tomorrow it is invalid
+ $daysUntilInvalid = 0;
+ $time = (string) floor((time() / (3600 * 24))); // get day number
+ if (isset($_COOKIE['otp'])) {
+ [$otpday, $hash] = explode(':', $_COOKIE['otp']);
+
+ if ($otpday >= $time - $daysUntilInvalid && $hash === hash_hmac('sha1', $this->getUsername().':'.$otpday.':'.$_SERVER['HTTP_USER_AGENT'], $this->getSecret())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/vendor/sonata-project/google-authenticator/sample/web/index.php b/vendor/sonata-project/google-authenticator/sample/web/index.php
new file mode 100644
index 0000000..626383f
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/web/index.php
@@ -0,0 +1,119 @@
+<?php declare(strict_types=1);
+ob_start(); //i'm too lazy to check when is sent what ;)
+//set session cookie to be read only via http and not by JavaScript
+ini_set('session.cookie_httponly', '1');
+
+include_once __DIR__.'/../../src/GoogleAuthenticator.php';
+include_once __DIR__.'/../../src/GoogleQrUrl.php';
+include_once __DIR__.'/../../src/FixedBitNotation.php';
+include_once 'Users.php';
+
+?>
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Google Authenticator in PHP demo</title>
+</head>
+<body>
+<?php
+
+//set this to false, if you don't want the token prefilled
+$debug = true;
+
+$users = new Users();
+//check if the user has a session, if not, show the login screen
+if ($username = $users->hasSession()) {
+ //load the user data from the json storage.
+ $user = $users->loadUser($username);
+ //if he clicked logout, destroy the session and redirect to the startscreen.
+ if (isset($_GET['logout'])) {
+ session_destroy();
+ header('Location: ./');
+ }
+ // check if the user is logged in.
+ if ($user->isLoggedIn()) {
+ include __DIR__.'/../tmpl/loggedin.php';
+ //show the QR code if whished so
+ if (isset($_GET['showqr'])) {
+ $secret = $user->getSecret();
+ include __DIR__.'/../tmpl/show-qr.php';
+ }
+ }
+ //if the user is in the OTP phase and submit the OTP.
+ else {
+ if ($user->isOTP() && isset($_POST['otp'])) {
+ $g = new \Google\Authenticator\GoogleAuthenticator();
+ // check if the submitted token is the right one and log in
+ if ($g->checkCode($user->getSecret(), $_POST['otp'])) {
+ // do log-in the user
+ $user->doLogin();
+ //if the user clicked the "remember the token" checkbox, set the cookie
+ if (isset($_POST['remember']) && $_POST['remember']) {
+ $user->setOTPCookie();
+ }
+ include __DIR__.'/../tmpl/loggedin.php';
+ }
+ //if the OTP is wrong, destroy the session and tell the user to try again
+ else {
+ session_destroy();
+ include __DIR__.'/../tmpl/login-error.php';
+ }
+ }
+ // if the user is neither logged in nor in the OTP phase, show the login form
+ else {
+ session_destroy();
+ include __DIR__.'/../tmpl/login.php';
+ }
+ }
+ exit();
+}
+ //if the username is set in _POST, then we assume the user filled in the login form.
+
+ if (isset($_POST['username'])) {
+ // check if we can load the user (ie. the user exists in our db)
+ $user = $users->loadUser($_POST['username']);
+ if ($user) {
+ //try to authenticate the password and start the session if it's correct.
+ if ($user->auth($_POST['password'])) {
+ $user->startSession();
+ //check if the user has a valid OTP cookie, so we don't have to
+ // ask for the current token and can directly log in
+ if ($user->hasValidOTPCookie()) {
+ include __DIR__.'/../tmpl/loggedin.php';
+ $user->doLogin();
+ }
+ // try to get the users' secret from the db,
+ // if he doesn't have one, generate one, store it and show it.
+ else {
+ if (!$user->getSecret()) {
+ include __DIR__.'/../tmpl/loggedin.php';
+
+ $secret = $user->generateSecret();
+ $users->storeData($user);
+ $user->doLogin();
+ include __DIR__.'/../tmpl/show-qr.php';
+ }
+ // if the user neither has a valid OTP cookie nor it's the first login
+ // ask for the OTP
+ else {
+ $user->doOTP();
+ include __DIR__.'/../tmpl/ask-for-otp.php';
+ }
+ }
+
+ exit();
+ }
+ }
+ // if we're here, something went wrong, destroy the session and show a login error
+ session_destroy();
+
+ include __DIR__.'/../tmpl/login-error.php';
+ exit();
+ }
+
+// if neither a session nor tried to submit the login credentials -> login screen
+include __DIR__.'/../tmpl/login.php';
+
+?>
+</body>
+</html>
diff --git a/vendor/sonata-project/google-authenticator/src/FixedBitNotation.php b/vendor/sonata-project/google-authenticator/src/FixedBitNotation.php
new file mode 100644
index 0000000..5676dd3
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/src/FixedBitNotation.php
@@ -0,0 +1,292 @@
+<?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);
diff --git a/vendor/sonata-project/google-authenticator/src/GoogleAuthenticator.php b/vendor/sonata-project/google-authenticator/src/GoogleAuthenticator.php
new file mode 100644
index 0000000..52f0598
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/src/GoogleAuthenticator.php
@@ -0,0 +1,178 @@
+<?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);
diff --git a/vendor/sonata-project/google-authenticator/src/GoogleAuthenticatorInterface.php b/vendor/sonata-project/google-authenticator/src/GoogleAuthenticatorInterface.php
new file mode 100644
index 0000000..b3144fd
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/src/GoogleAuthenticatorInterface.php
@@ -0,0 +1,44 @@
+<?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;
+}
diff --git a/vendor/sonata-project/google-authenticator/src/GoogleQrUrl.php b/vendor/sonata-project/google-authenticator/src/GoogleQrUrl.php
new file mode 100644
index 0000000..413d226
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/src/GoogleQrUrl.php
@@ -0,0 +1,93 @@
+<?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);
diff --git a/vendor/sonata-project/google-authenticator/src/RuntimeException.php b/vendor/sonata-project/google-authenticator/src/RuntimeException.php
new file mode 100644
index 0000000..4d3cf5f
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/src/RuntimeException.php
@@ -0,0 +1,46 @@
+<?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);