diff --git a/app/controllers/Detail.php b/app/controllers/Detail.php index 2441e0d59d8f60fafcc3b877bfea8bf6566252de..6799ca97ed749df26564eacf81de0bc0f3756c9a 100644 --- a/app/controllers/Detail.php +++ b/app/controllers/Detail.php @@ -66,7 +66,7 @@ class Detail extends Controller $entityBody = json_decode(file_get_contents('php://input'), true); //TODO MASUKKIN KARTU NASABAH ID $user = $model_user->readUserById($user_id); - $orderid = $soap->buyBook($_COOKIE['bookid'], $entityBody['total'], $user['no_kartu']); + $orderid = $soap->buyBook($_COOKIE['bookid'], $entityBody['total'], $user['no_kartu'], $entityBody['token']); if ($orderid != -1) { $model->createOrder($_COOKIE['bookid'], $user_id, $orderid); } diff --git a/app/models/SoapHelper.php b/app/models/SoapHelper.php index e391860a458b5d2b5ae7e8b14eda5752e78db206..bac9da126db9435c86ad7c6c599a2559406d11e8 100644 --- a/app/models/SoapHelper.php +++ b/app/models/SoapHelper.php @@ -49,8 +49,8 @@ class SoapHelper { return $output; } - public function buyBook($id, $quantity, $no_rek) { - $data = $this->conn->buyBook($id, $quantity, $no_rek); + public function buyBook($id, $quantity, $no_rek, $token) { + $data = $this->conn->buyBook($id, $quantity, $no_rek, $token); $array = json_decode(json_encode($data), True); return $array; } diff --git a/app/models/Token.php b/app/models/Token.php index 5828e971c9381943ff8c3b36d274884983fbf89e..0bbed2b6d60999544a7d6228980c3cc29118fc9b 100644 --- a/app/models/Token.php +++ b/app/models/Token.php @@ -24,6 +24,8 @@ class Token extends Model $ip_address = $_SERVER['REMOTE_ADDR']; $result = $this->conn->query($sql)->fetch_assoc(); if ($result['id'] && $result['browser'] == $browser && $result['ip_address'] == $ip_address && time() < (int)$result['time']) { + $sql = "UPDATE access_token SET time = " . (time() + (int)1200) . " WHERE token = '" . $token . "';" ; + $this->conn->query($sql); return $result['id']; } else { return null; diff --git a/app/views/detail.php b/app/views/detail.php index 6ca7e33b22fead9478d58c8054c58dc11511151b..ca4457cacd07340cc0fd77da5c87c2513c9de8a9 100644 --- a/app/views/detail.php +++ b/app/views/detail.php @@ -52,6 +52,11 @@ <?php } ?> </select> </div> + <label for="token" class="inp"> + <input type="text" id="token" placeholder=" "> + <span class="label">Token</span> + <span class="border"></span> + </label> <button onclick="order()" <?php if ($data['book']['price'] == -1) {echo "disabled";}?>>Order</button> </section> <section id="reviews"> @@ -98,7 +103,7 @@ <div id="dialog-msg"> <img id="check-notif" src="/public/images/check.png"> <div id="msg-detail"> - <p>Pemesanan Berhasil!</p> + <p id="text-msg-detail">Pemesanan Berhasil!</p> <p>Nomor Transaksi : <span id="transaction-number">3<span></p> </div> <img id="close-notif" onclick="close_notif()" src="/public/images/close.png"> diff --git a/composer.json b/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..707632dcae8d2de97e4d6883184911888438f4f6 --- /dev/null +++ b/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "sonata-project/google-authenticator": "^2.2" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000000000000000000000000000000000..f36ec219428a511a46ef23ef81b8d453f0d0bcde --- /dev/null +++ b/composer.lock @@ -0,0 +1,75 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "e2df69fcfa963bd7ffe8e358d7e93d70", + "packages": [ + { + "name": "sonata-project/google-authenticator", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/sonata-project/GoogleAuthenticator.git", + "reference": "feda53899b26af24e3db2fe7a3e5f053ca483762" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sonata-project/GoogleAuthenticator/zipball/feda53899b26af24e3db2fe7a3e5f053ca483762", + "reference": "feda53899b26af24e3db2fe7a3e5f053ca483762", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Google\\Authenticator\\": "src/", + "Sonata\\GoogleAuthenticator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Stocker", + "email": "me@chregu.tv" + }, + { + "name": "Andre DeMarre", + "homepage": "http://www.devnetwork.net/viewtopic.php?f=50&t=94989" + }, + { + "name": "Thomas Rabaix", + "email": "thomas.rabaix@gmail.com" + } + ], + "description": "Library to integrate Google Authenticator into a PHP project", + "homepage": "https://github.com/sonata-project/GoogleAuthenticator", + "keywords": [ + "google authenticator" + ], + "time": "2018-07-18T22:08:02+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/example.php b/example.php new file mode 100644 index 0000000000000000000000000000000000000000..50366d2fa9c470a34eb2474568393a4642a0ad78 --- /dev/null +++ b/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/index.php b/index.php index afa4a1aba47c70e2c90aea4438d7feb3d3673b83..996aa32a611deba4c23295a3f6db6b5f0bf47503 100644 --- a/index.php +++ b/index.php @@ -1,5 +1,6 @@ <?php ini_set('display_errors', 0); +require_once 'vendor/autoload.php'; require_once 'app/init.php'; diff --git a/public/css/main.css b/public/css/main.css index 7bfd34b9d22487c2d17c8ba9e1c19c5793f86d2e..c9ce394755783299421cdd3feb46ed4635dd4d19 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -1034,4 +1034,74 @@ body { height: 200px !important; -webkit-transform: translate(-100px, -100px) scale(1) translate(100px, 100px); transform: translate(-100px, -100px) scale(1) translate(100px, 100px); +} + +.qr-code{ + display: block; + text-align: center; + width: 100%; + height: 100%; + margin: auto; + border-radius: 3px; +} + +* .inp { + position: relative; + margin: auto; + width: 100%; + max-width: 280px; +} +* .inp .label { + position: absolute; + top: 16px; + left: 0; + font-size: 16px; + color: #9098a9; + font-weight: 500; + transform-origin: 0 0; + transition: all 0.2s ease; +} +* .inp .border { + position: absolute; + bottom: 0; + left: 0; + height: 2px; + width: 100%; + background: #07f; + transform: scaleX(0); + transform-origin: 0 0; + transition: all 0.15s ease; +} +* .inp input { + -webkit-appearance: none; + width: 100%; + border: 0; + font-family: inherit; + padding: 12px 0; + height: 48px; + font-size: 16px; + font-weight: 500; + border-bottom: 2px solid #c8ccd4; + background: none; + border-radius: 0; + color: #223254; + transition: all 0.15s ease; +} +* .inp input:hover { + background: rgba(34,50,84,0.03); +} +* .inp input:not(:placeholder-shown) + span { + color: #5a667f; + transform: translateY(-26px) scale(0.75); +} +* .inp input:focus { + background: none; + outline: none; +} +* .inp input:focus + span { + color: #07f; + transform: translateY(-26px) scale(0.75); +} +* .inp input:focus + span + .border { + transform: scaleX(1); } \ No newline at end of file diff --git a/public/images/profile/32 b/public/images/profile/32 new file mode 100644 index 0000000000000000000000000000000000000000..27cac80db760b8d73f20e7d0401ee5f3b1e5e530 Binary files /dev/null and b/public/images/profile/32 differ diff --git a/public/js/detail.js b/public/js/detail.js index f3d3a3cd777d45704ea1ef3b49b310fc7eaee87f..c0ddcce64d299c92f8c1ce866ed8c01267770417 100644 --- a/public/js/detail.js +++ b/public/js/detail.js @@ -1,5 +1,7 @@ function order() { var total_order_element = document.getElementById("total-order"); + var token = document.getElementById("token").value; + console.log("token: "+token); var sum_order = total_order_element.options[total_order_element.selectedIndex].value; var xhttp = new XMLHttpRequest(); xhttp.open("POST", "/detail/order"); @@ -7,11 +9,21 @@ function order() { xhttp.onreadystatechange = function() { console.log(this.responseText); if (this.readyState == 4 && this.status == 200 && this.responseText != -1) { + document.getElementById("text-msg-detail").innerHTML = "Pemesanan Berhasil!"; document.getElementById("transaction-number").innerHTML = this.responseText; document.getElementById("notif").setAttribute("style", "display: flex"); + document.getElementById("check-notif").src = "/public/images/check.png"; + } else { + document.getElementById("text-msg-detail").innerHTML = "Pemesanan Gagal!"; + document.getElementById("transaction-number").innerHTML = -1; + document.getElementById("notif").setAttribute("style", "display: flex"); + document.getElementById("check-notif").src = "/public/images/close.png"; + } } - xhttp.send(JSON.stringify({total : sum_order})); + xhttp.send(JSON.stringify({ + total : sum_order, + token : token})); } function close_notif() { diff --git a/public/js/profile.js b/public/js/profile.js new file mode 100644 index 0000000000000000000000000000000000000000..d26153eb0e2a39618d3132309d02ee6a54f6d6b8 --- /dev/null +++ b/public/js/profile.js @@ -0,0 +1,11 @@ +// console.log("No kartu: " + document.getElementById("no-kartu").value); +var xhttp = new XMLHttpRequest(); +xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + var response = JSON.parse(this.responseText); + document.getElementById("qr-img").src = response.values + } +} +xhttp.open("POST", "http://localhost:3000/qrcode", true); +xhttp.setRequestHeader("Content-type", "application/json"); +xhttp.send(JSON.stringify({ "no_kartu": document.getElementById("no-kartu").value })); diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000000000000000000000000000000000000..46ffb4ce11adee005348f0eb20d6189078f6ac8f --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,7 @@ +<?php + +// autoload.php @generated by Composer + +require_once __DIR__ . '/composer/autoload_real.php'; + +return ComposerAutoloaderInitae14d3cd7eb3d58ce5dbc9a906ade70c::getLoader(); diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php new file mode 100644 index 0000000000000000000000000000000000000000..fce8549f0781bafdc7da2301b84d048286757445 --- /dev/null +++ b/vendor/composer/ClassLoader.php @@ -0,0 +1,445 @@ +<?php + +/* + * This file is part of Composer. + * + * (c) Nils Adermann <naderman@naderman.de> + * Jordi Boggiano <j.boggiano@seld.be> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Jordi Boggiano <j.boggiano@seld.be> + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + private $classMapAuthoritative = false; + private $missingClasses = array(); + private $apcuPrefix; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..f27399a042d95c4708af3a8c74d35d338763cf8f --- /dev/null +++ b/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +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/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000000000000000000000000000000000000..7a91153b0d8ea10bc693176a81d8a9eb96883a76 --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,9 @@ +<?php + +// autoload_classmap.php @generated by Composer + +$vendorDir = dirname(dirname(__FILE__)); +$baseDir = dirname($vendorDir); + +return array( +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000000000000000000000000000000000000..b7fc0125dbca56fd7565ad62097672a59473e64e --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ +<?php + +// autoload_namespaces.php @generated by Composer + +$vendorDir = dirname(dirname(__FILE__)); +$baseDir = dirname($vendorDir); + +return array( +); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php new file mode 100644 index 0000000000000000000000000000000000000000..c57cc2d8fb15c2c170b30ca1e7176b7b427ac591 --- /dev/null +++ b/vendor/composer/autoload_psr4.php @@ -0,0 +1,11 @@ +<?php + +// autoload_psr4.php @generated by Composer + +$vendorDir = dirname(dirname(__FILE__)); +$baseDir = dirname($vendorDir); + +return array( + 'Sonata\\GoogleAuthenticator\\' => array($vendorDir . '/sonata-project/google-authenticator/src'), + 'Google\\Authenticator\\' => array($vendorDir . '/sonata-project/google-authenticator/src'), +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 0000000000000000000000000000000000000000..6cd5c1f9cc557f31c8346d95f0dfe9cea48f8d7a --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,52 @@ +<?php + +// autoload_real.php @generated by Composer + +class ComposerAutoloaderInitae14d3cd7eb3d58ce5dbc9a906ade70c +{ + private static $loader; + + public static function loadClassLoader($class) + { + if ('Composer\Autoload\ClassLoader' === $class) { + require __DIR__ . '/ClassLoader.php'; + } + } + + public static function getLoader() + { + if (null !== self::$loader) { + return self::$loader; + } + + spl_autoload_register(array('ComposerAutoloaderInitae14d3cd7eb3d58ce5dbc9a906ade70c', 'loadClassLoader'), true, true); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + spl_autoload_unregister(array('ComposerAutoloaderInitae14d3cd7eb3d58ce5dbc9a906ade70c', 'loadClassLoader')); + + $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInitae14d3cd7eb3d58ce5dbc9a906ade70c::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->register(true); + + return $loader; + } +} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php new file mode 100644 index 0000000000000000000000000000000000000000..20899b7474c2f76f372ce0ab6b481a0fb5ceb782 --- /dev/null +++ b/vendor/composer/autoload_static.php @@ -0,0 +1,39 @@ +<?php + +// autoload_static.php @generated by Composer + +namespace Composer\Autoload; + +class ComposerStaticInitae14d3cd7eb3d58ce5dbc9a906ade70c +{ + public static $prefixLengthsPsr4 = array ( + 'S' => + array ( + 'Sonata\\GoogleAuthenticator\\' => 27, + ), + 'G' => + array ( + 'Google\\Authenticator\\' => 21, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Sonata\\GoogleAuthenticator\\' => + array ( + 0 => __DIR__ . '/..' . '/sonata-project/google-authenticator/src', + ), + 'Google\\Authenticator\\' => + array ( + 0 => __DIR__ . '/..' . '/sonata-project/google-authenticator/src', + ), + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInitae14d3cd7eb3d58ce5dbc9a906ade70c::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInitae14d3cd7eb3d58ce5dbc9a906ade70c::$prefixDirsPsr4; + + }, null, ClassLoader::class); + } +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 0000000000000000000000000000000000000000..b6b2711fd134f8028c4db588065cf44c51ab00ff --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,61 @@ +[ + { + "name": "sonata-project/google-authenticator", + "version": "2.2.0", + "version_normalized": "2.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sonata-project/GoogleAuthenticator.git", + "reference": "feda53899b26af24e3db2fe7a3e5f053ca483762" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sonata-project/GoogleAuthenticator/zipball/feda53899b26af24e3db2fe7a3e5f053ca483762", + "reference": "feda53899b26af24e3db2fe7a3e5f053ca483762", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.0" + }, + "time": "2018-07-18T22:08:02+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Google\\Authenticator\\": "src/", + "Sonata\\GoogleAuthenticator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Stocker", + "email": "me@chregu.tv" + }, + { + "name": "Andre DeMarre", + "homepage": "http://www.devnetwork.net/viewtopic.php?f=50&t=94989" + }, + { + "name": "Thomas Rabaix", + "email": "thomas.rabaix@gmail.com" + } + ], + "description": "Library to integrate Google Authenticator into a PHP project", + "homepage": "https://github.com/sonata-project/GoogleAuthenticator", + "keywords": [ + "google authenticator" + ] + } +] diff --git a/vendor/sonata-project/google-authenticator/LICENSE b/vendor/sonata-project/google-authenticator/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..0135e0b6aebb4addef7246658ac1af1e56ba9b53 --- /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/Makefile b/vendor/sonata-project/google-authenticator/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..4ae0c63d810bcc5615b7da192ff0145a5a7e72c8 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/Makefile @@ -0,0 +1,60 @@ +# DO NOT EDIT THIS FILE! +# +# It's auto-generated by sonata-project/dev-kit package. + +all: + @echo "Please choose a task." +.PHONY: all + +lint: lint-composer lint-yaml lint-composer lint-xml lint-php +.PHONY: lint + +lint-composer: + composer validate +.PHONY: lint-composer + +lint-yaml: + yaml-lint --ignore-non-yaml-files --quiet --exclude vendor . + +.PHONY: lint-yaml + +lint-xml: + find . \( -name '*.xml' -or -name '*.xliff' \) \ + -not -path './vendor/*' \ + -not -path './src/Resources/public/vendor/*' \ + | while read xmlFile; \ + do \ + XMLLINT_INDENT=' ' xmllint --encode UTF-8 --format "$$xmlFile"|diff - "$$xmlFile"; \ + if [ $$? -ne 0 ] ;then exit 1; fi; \ + done + +.PHONY: lint-xml + +lint-php: + php-cs-fixer fix --ansi --verbose --diff --dry-run +.PHONY: lint-php + +cs-fix: cs-fix-php cs-fix-xml +.PHONY: cs-fix + +cs-fix-php: + php-cs-fixer fix --verbose +.PHONY: cs-fix-php + +cs-fix-xml: + find . \( -name '*.xml' -or -name '*.xliff' \) \ + -not -path './vendor/*' \ + -not -path './src/Resources/public/vendor/*' \ + | while read xmlFile; \ + do \ + XMLLINT_INDENT=' ' xmllint --encode UTF-8 --format "$$xmlFile" --output "$$xmlFile"; \ + done +.PHONY: cs-fix-xml + +test: + phpunit -c phpunit.xml.dist --coverage-clover build/logs/clover.xml +.PHONY: test + +docs: + cd docs && sphinx-build -W -b html -d _build/doctrees . _build/html +.PHONY: docs diff --git a/vendor/sonata-project/google-authenticator/composer.json b/vendor/sonata-project/google-authenticator/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..4fbb160acfb53ae77238e3a9b848ea7c7ae8277c --- /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.1" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.0" + }, + "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/phpunit.xml.dist b/vendor/sonata-project/google-authenticator/phpunit.xml.dist new file mode 100644 index 0000000000000000000000000000000000000000..492b036db0eb7cfd428de82bdf6e1303b56eefec --- /dev/null +++ b/vendor/sonata-project/google-authenticator/phpunit.xml.dist @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- +DO NOT EDIT THIS FILE! + +It's auto-generated by sonata-project/dev-kit package. +--> + +<phpunit backupGlobals="false" + backupStaticAttributes="false" + colors="true" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + processIsolation="false" + stopOnFailure="false" + syntaxCheck="false" + bootstrap="tests/bootstrap.php" +> + <testsuites> + <testsuite name="Sonata Google Authenticator Test Suite"> + <directory suffix="Test.php">./tests/</directory> + </testsuite> + </testsuites> + + <listeners> + <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" /> + </listeners> + + <filter> + <whitelist> + <directory suffix=".php">./src/</directory> + </whitelist> + </filter> + + <php> + <ini name="precision" value="8"/> + </php> + +</phpunit> 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 0000000000000000000000000000000000000000..50366d2fa9c470a34eb2474568393a4642a0ad78 --- /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 0000000000000000000000000000000000000000..f3e06d477e22f323426f97ae46c67be8b7a2efba --- /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 0000000000000000000000000000000000000000..2a19032ca4f1527b6e49c0128c4103799746e547 --- /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 0000000000000000000000000000000000000000..8d23fd33c7bd09a636c365d3c56e91fa70f08c59 --- /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 0000000000000000000000000000000000000000..fd81623009b4b8620b6d3c8e21111c93df2c182c --- /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 0000000000000000000000000000000000000000..774a298deed8458831ed231cb0680441223a4d0a --- /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 0000000000000000000000000000000000000000..fdcc130c43b31f2d7b337b977705a164826b9688 --- /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 0000000000000000000000000000000000000000..fb169def9245eae78279bf2c6a7357c6fc5e71a8 --- /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'])) { + list($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 0000000000000000000000000000000000000000..c039674c53f53691340e7c3a6f4bb1c236d94335 --- /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'; + } + } + die(); +} + //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'; + } + } + + die(); + } + } + // if we're here, something went wrong, destroy the session and show a login error + session_destroy(); + + include __DIR__.'/../tmpl/login-error.php'; + die(); + } + +// 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 0000000000000000000000000000000000000000..3f5ff558b05e8a721dc4589796154bcd2d65d574 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/src/FixedBitNotation.php @@ -0,0 +1,296 @@ +<?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 + * + * @return string + */ + 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 = $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 + * + * @return string + */ + 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 0000000000000000000000000000000000000000..145739c54bc369c59d8863649968cb5c980b3230 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/src/GoogleAuthenticator.php @@ -0,0 +1,173 @@ +<?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 $now; + + /** + * @var int + */ + private $codePeriod = 30; + + /** + * @param int $passCodeLength + * @param int $secretLength + * @param \DateTimeInterface|null $now + */ + public function __construct(int $passCodeLength = 6, int $secretLength = 10, \DateTimeInterface $now = null) + { + $this->passCodeLength = $passCodeLength; + $this->secretLength = $secretLength; + $this->pinModulo = 10 ** $passCodeLength; + $this->now = $now ?? new \DateTimeImmutable(); + } + + /** + * @param string $secret + * @param string $code + */ + public function checkCode($secret, $code): bool + { + /** + * The result of each comparison is accumulated 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. + */ + $result = 0; + + // current period + $result += hash_equals($this->getCode($secret, $this->now), $code); + + // previous period, happens if the user was slow to enter or it just crossed over + $dateTime = new \DateTimeImmutable('@'.($this->now->getTimestamp() - $this->codePeriod)); + $result += hash_equals($this->getCode($secret, $dateTime), $code); + + // next period, happens if the user is not completely synced and possibly a few seconds ahead + $dateTime = new \DateTimeImmutable('@'.($this->now->getTimestamp() + $this->codePeriod)); + $result += hash_equals($this->getCode($secret, $dateTime), $code); + + return $result > 0; + } + + /** + * NEXT_MAJOR: add the interface typehint to $time and remove deprecation. + * + * @param string $secret + * @param float|string|int|null|\DateTimeInterface $time + */ + public function getCode($secret, /* \DateTimeInterface */$time = null): string + { + if (null === $time) { + $time = $this->now; + } + + if ($time instanceof \DateTimeInterface) { + $timeForCode = floor($time->getTimestamp() / $this->codePeriod); + } 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)); + } + + /** + * @param string $bytes + * @param int $start + */ + 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 0000000000000000000000000000000000000000..70219f2090524c8549410973634cc5d7c06fe05a --- /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): bool; + + /** + * NEXT_MAJOR: add the interface typehint to $time and remove deprecation. + * + * @param string $secret + * @param float|string|int|null|\DateTimeInterface $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 0000000000000000000000000000000000000000..7e9a537a4aa63f764233da690e2dd40e14851ef9 --- /dev/null +++ b/vendor/sonata-project/google-authenticator/src/GoogleQrUrl.php @@ -0,0 +1,94 @@ +<?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 https://developers.google.com/chart/infographics/docs/qr_codes + * @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 + * + * @return string + */ + 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://chart.googleapis.com/chart?chs=%1$dx%1$d&chld=M|0&cht=qr&chl=%2$s', + $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 0000000000000000000000000000000000000000..4d3cf5f63f2d3137dd8e9bc7095e8410ff49414d --- /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);