diff --git a/.env.example b/.env.example index 27f6db4bee42..c102a794856b 100644 --- a/.env.example +++ b/.env.example @@ -37,3 +37,5 @@ PUSHER_APP_CLUSTER=mt1 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" + +AUTH_PROVIDER= diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 9e68caa6f3c5..edc5b53d0025 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; +use Illuminate\Support\Facades\Auth; class AuthServiceProvider extends ServiceProvider { @@ -24,6 +25,8 @@ class AuthServiceProvider extends ServiceProvider { $this->registerPolicies(); - // + Auth::provider('multidb', function ($app, array $config) { + return new MultiDatabaseUserProvider($this->app['db']->connection(), $this->app['hash'], 'users'); + }); } } diff --git a/app/Providers/MultiDatabaseUserProvider.php b/app/Providers/MultiDatabaseUserProvider.php new file mode 100644 index 000000000000..ffb73cbe7faf --- /dev/null +++ b/app/Providers/MultiDatabaseUserProvider.php @@ -0,0 +1,227 @@ +conn = $conn; + $this->table = $table; + $this->hasher = $hasher; + } + + /** + * Retrieve a user by their unique identifier. + * + * @param mixed $identifier + * + * @return \Illuminate\Contracts\Auth\Authenticatable|null + */ + public function retrieveById($identifier) + { + $this->setDefaultDatabase($identifier); + + $user = $this->conn->table($this->table)->find($identifier); + + return $this->getGenericUser($user); + } + + /** + * Retrieve a user by their unique identifier and "remember me" token. + * + * @param mixed $identifier + * @param string $token + * + * @return \Illuminate\Contracts\Auth\Authenticatable|null + */ + public function retrieveByToken($identifier, $token) + { + $this->setDefaultDatabase($identifier, false, $token); + + $user = $this->conn->table($this->table) + ->where('id', $identifier) + ->where('remember_token', $token) + ->first(); + + return $this->getGenericUser($user); + } + + /** + * Update the "remember me" token for the given user in storage. + * + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param string $token + * + * @return void + */ + public function updateRememberToken(UserContract $user, $token) + { + $this->conn->table($this->table) + ->where('id', $user->getAuthIdentifier()) + ->update(['remember_token' => $token]); + } + + /** + * Retrieve a user by the given credentials. + * + * @param array $credentials + * + * @return \Illuminate\Contracts\Auth\Authenticatable|null + */ + public function retrieveByCredentials(array $credentials) + { + /* + * We use the email address to determine which serveer to link up. + */ + + foreach ($credentials as $key => $value) { + if (Str::contains($key, 'email')) { + $this->setDefaultDatabase(false, $value, false); + } + } + + /** + * | Build query. + */ + $query = $this->conn->table($this->table); + + foreach ($credentials as $key => $value) { + if (!Str::contains($key, 'password')) { + $query->where($key, $value); + } + } + + // Now we are ready to execute the query to see if we have an user matching + // the given credentials. If not, we will just return nulls and indicate + // that there are no matching users for these given credential arrays. + $user = $query->first(); + + return $this->getGenericUser($user); + } + + /** + * Get the generic user. + * + * @param mixed $user + * + * @return \Illuminate\Auth\GenericUser|null + */ + protected function getGenericUser($user) + { + if (!is_null($user)) { + return new GenericUser((array) $user); + } + } + + /** + * Validate a user against the given credentials. + * + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param array $credentials + * + * @return bool + */ + public function validateCredentials(UserContract $user, array $credentials) + { + return $this->hasher->check( + $credentials['password'], $user->getAuthPassword() + ); + } + + /** + * @param (int) $id + * @param string $username + * @param string $token + * + * @return void + */ + private function setDefaultDatabase($id = false, $username = false, $token = false) : void + { + $databases = ['db-ninja-1', 'db-ninja-2']; + + foreach ($databases as $database) { + $this->setDB($database); + //Log::error('database name = '. DB::getDatabaseName()); + + $query = $this->conn->table('users'); + + if ($id) { + $query->where('id', '=', $id); + } + + if ($token) { + $query->where('token', '=', $token); + } + + if ($username) { + $query->where('email', '=', $username); + } + + $user = $query->get(); + + // Log::error(print_r($user,1)); + // Log::error($database); + + if (count($user) >= 1) { + Log::error('found a DB!'); + break; + } + } + } + + private function setDB($database) + { + /** Get the database name we want to switch to*/ + $db_name = config('database.connections.'.$database.'.database'); + //$db_host = config("database.connections.".$database.".db_host"); + + /* This will set the default configuration for the request / session?*/ + config(['database.default' => $database]); + + /* Set the connection to complete the user authentication */ + //$this->conn = app('db')->connection(config("database.connections.database." . $database . "." . $db_name)); + $this->conn = app('db')->connection(config('database.connections.database.'.$database)); + } +} diff --git a/c3.php b/c3.php deleted file mode 100644 index bea4faea5bf7..000000000000 --- a/c3.php +++ /dev/null @@ -1,336 +0,0 @@ - $value) { - $_SERVER['HTTP_X_CODECEPTION_'.strtoupper($key)] = $value; - } - } -} -if (!array_key_exists('HTTP_X_CODECEPTION_CODECOVERAGE', $_SERVER)) { - return; -} -if (!function_exists('__c3_error')) { - function __c3_error($message) - { - $errorLogFile = defined('C3_CODECOVERAGE_ERROR_LOG_FILE') ? - C3_CODECOVERAGE_ERROR_LOG_FILE : - C3_CODECOVERAGE_MEDIATE_STORAGE.DIRECTORY_SEPARATOR.'error.txt'; - if (is_writable($errorLogFile)) { - file_put_contents($errorLogFile, $message); - } else { - $message = "Could not write error to log file ($errorLogFile), original message: $message"; - } - if (!headers_sent()) { - header('X-Codeception-CodeCoverage-Error: '.str_replace("\n", ' ', $message), true, 500); - } - setcookie('CODECEPTION_CODECOVERAGE_ERROR', $message); - } -} -// phpunit codecoverage shimming -if (!class_exists('PHP_CodeCoverage') and class_exists('SebastianBergmann\CodeCoverage\CodeCoverage')) { - class_alias('SebastianBergmann\CodeCoverage\CodeCoverage', 'PHP_CodeCoverage'); - class_alias('SebastianBergmann\CodeCoverage\Report\Text', 'PHP_CodeCoverage_Report_Text'); - class_alias('SebastianBergmann\CodeCoverage\Report\PHP', 'PHP_CodeCoverage_Report_PHP'); - class_alias('SebastianBergmann\CodeCoverage\Report\Clover', 'PHP_CodeCoverage_Report_Clover'); - class_alias('SebastianBergmann\CodeCoverage\Report\Crap4j', 'PHP_CodeCoverage_Report_Crap4j'); - class_alias('SebastianBergmann\CodeCoverage\Report\Html\Facade', 'PHP_CodeCoverage_Report_HTML'); - class_alias('SebastianBergmann\CodeCoverage\Report\Xml\Facade', 'PHP_CodeCoverage_Report_XML'); - class_alias('SebastianBergmann\CodeCoverage\Exception', 'PHP_CodeCoverage_Exception'); -} -// phpunit version -if (!class_exists('PHPUnit_Runner_Version') && class_exists('PHPUnit\Runner\Version')) { - class_alias('PHPUnit\Runner\Version', 'PHPUnit_Runner_Version'); -} -// Autoload Codeception classes -if (!class_exists('\\Codeception\\Codecept')) { - if (file_exists(__DIR__.'/codecept.phar')) { - require_once 'phar://'.__DIR__.'/codecept.phar/autoload.php'; - } elseif (stream_resolve_include_path(__DIR__.'/vendor/autoload.php')) { - require_once __DIR__.'/vendor/autoload.php'; - // Required to load some methods only available at codeception/autoload.php - if (stream_resolve_include_path(__DIR__.'/vendor/codeception/codeception/autoload.php')) { - require_once __DIR__.'/vendor/codeception/codeception/autoload.php'; - } - } elseif (stream_resolve_include_path('Codeception/autoload.php')) { - require_once 'Codeception/autoload.php'; - } else { - __c3_error('Codeception is not loaded. Please check that either PHAR or Composer package can be used'); - } -} -// Load Codeception Config -$config_dist_file = realpath(__DIR__).DIRECTORY_SEPARATOR.'codeception.dist.yml'; -$config_file = realpath(__DIR__).DIRECTORY_SEPARATOR.'codeception.yml'; -if (isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_CONFIG'])) { - $config_file = realpath(__DIR__).DIRECTORY_SEPARATOR.$_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_CONFIG']; -} -if (file_exists($config_file)) { - // Use codeception.yml for configuration. -} elseif (file_exists($config_dist_file)) { - // Use codeception.dist.yml for configuration. - $config_file = $config_dist_file; -} else { - __c3_error(sprintf("Codeception config file '%s' not found", $config_file)); -} - -try { - \Codeception\Configuration::config($config_file); -} catch (\Exception $e) { - __c3_error($e->getMessage()); -} -if (!defined('C3_CODECOVERAGE_MEDIATE_STORAGE')) { - // workaround for 'zend_mm_heap corrupted' problem - gc_disable(); - $memoryLimit = ini_get('memory_limit'); - $requiredMemory = '384M'; - if ((substr($memoryLimit, -1) === 'M' && (int) $memoryLimit < (int) $requiredMemory) - || (substr($memoryLimit, -1) === 'K' && (int) $memoryLimit < (int) $requiredMemory * 1024) - || (ctype_digit($memoryLimit) && (int) $memoryLimit < (int) $requiredMemory * 1024 * 1024) - ) { - ini_set('memory_limit', $requiredMemory); - } - define('C3_CODECOVERAGE_MEDIATE_STORAGE', Codeception\Configuration::logDir().'c3tmp'); - define('C3_CODECOVERAGE_PROJECT_ROOT', Codeception\Configuration::projectDir()); - define('C3_CODECOVERAGE_TESTNAME', $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE']); - function __c3_build_html_report(PHP_CodeCoverage $codeCoverage, $path) - { - $writer = new PHP_CodeCoverage_Report_HTML(); - $writer->process($codeCoverage, $path.'html'); - if (file_exists($path.'.tar')) { - unlink($path.'.tar'); - } - $phar = new PharData($path.'.tar'); - $phar->setSignatureAlgorithm(Phar::SHA1); - $files = $phar->buildFromDirectory($path.'html'); - array_map('unlink', $files); - if (in_array('GZ', Phar::getSupportedCompression())) { - if (file_exists($path.'.tar.gz')) { - unlink($path.'.tar.gz'); - } - $phar->compress(\Phar::GZ); - // close the file so that we can rename it - unset($phar); - unlink($path.'.tar'); - rename($path.'.tar.gz', $path.'.tar'); - } - - return $path.'.tar'; - } - function __c3_build_clover_report(PHP_CodeCoverage $codeCoverage, $path) - { - $writer = new PHP_CodeCoverage_Report_Clover(); - $writer->process($codeCoverage, $path.'.clover.xml'); - - return $path.'.clover.xml'; - } - function __c3_build_crap4j_report(PHP_CodeCoverage $codeCoverage, $path) - { - $writer = new PHP_CodeCoverage_Report_Crap4j(); - $writer->process($codeCoverage, $path.'.crap4j.xml'); - - return $path.'.crap4j.xml'; - } - function __c3_build_phpunit_report(PHP_CodeCoverage $codeCoverage, $path) - { - $writer = new PHP_CodeCoverage_Report_XML(\PHPUnit_Runner_Version::id()); - $writer->process($codeCoverage, $path.'phpunit'); - if (file_exists($path.'.tar')) { - unlink($path.'.tar'); - } - $phar = new PharData($path.'.tar'); - $phar->setSignatureAlgorithm(Phar::SHA1); - $files = $phar->buildFromDirectory($path.'phpunit'); - array_map('unlink', $files); - if (in_array('GZ', Phar::getSupportedCompression())) { - if (file_exists($path.'.tar.gz')) { - unlink($path.'.tar.gz'); - } - $phar->compress(\Phar::GZ); - // close the file so that we can rename it - unset($phar); - unlink($path.'.tar'); - rename($path.'.tar.gz', $path.'.tar'); - } - - return $path.'.tar'; - } - function __c3_send_file($filename) - { - if (!headers_sent()) { - readfile($filename); - } - - return __c3_exit(); - } - /** - * @param $filename - * @param bool $lock Lock the file for writing? - * - * @return [null|PHP_CodeCoverage|\SebastianBergmann\CodeCoverage\CodeCoverage, resource] - */ - function __c3_factory($filename, $lock = false) - { - $file = null; - if ($filename !== null && is_readable($filename)) { - if ($lock) { - $file = fopen($filename, 'r+'); - if (flock($file, LOCK_EX)) { - $phpCoverage = unserialize(stream_get_contents($file)); - } else { - __c3_error("Failed to acquire write-lock for $filename"); - } - } else { - $phpCoverage = unserialize(file_get_contents($filename)); - } - - return [$phpCoverage, $file]; - } else { - $phpCoverage = new PHP_CodeCoverage(); - } - if (isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_SUITE'])) { - $suite = $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_SUITE']; - - try { - $settings = \Codeception\Configuration::suiteSettings($suite, \Codeception\Configuration::config()); - } catch (Exception $e) { - __c3_error($e->getMessage()); - } - } else { - $settings = \Codeception\Configuration::config(); - } - - try { - \Codeception\Coverage\Filter::setup($phpCoverage) - ->whiteList($settings) - ->blackList($settings); - } catch (Exception $e) { - __c3_error($e->getMessage()); - } - - return [$phpCoverage, $file]; - } - function __c3_exit() - { - if (!isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG'])) { - exit; - } - } - function __c3_clear() - { - \Codeception\Util\FileSystem::doEmptyDir(C3_CODECOVERAGE_MEDIATE_STORAGE); - } -} -if (!is_dir(C3_CODECOVERAGE_MEDIATE_STORAGE)) { - if (mkdir(C3_CODECOVERAGE_MEDIATE_STORAGE, 0777, true) === false) { - __c3_error('Failed to create directory "'.C3_CODECOVERAGE_MEDIATE_STORAGE.'"'); - } -} -// evaluate base path for c3-related files -$path = realpath(C3_CODECOVERAGE_MEDIATE_STORAGE).DIRECTORY_SEPARATOR.'codecoverage'; -$requested_c3_report = (strpos($_SERVER['REQUEST_URI'], 'c3/report') !== false); -$complete_report = $current_report = $path.'.serialized'; -if ($requested_c3_report) { - set_time_limit(0); - $route = ltrim(strrchr($_SERVER['REQUEST_URI'], '/'), '/'); - if ($route === 'clear') { - __c3_clear(); - - return __c3_exit(); - } - list($codeCoverage) = __c3_factory($complete_report); - switch ($route) { - case 'html': - try { - __c3_send_file(__c3_build_html_report($codeCoverage, $path)); - } catch (Exception $e) { - __c3_error($e->getMessage()); - } - - return __c3_exit(); - case 'clover': - try { - __c3_send_file(__c3_build_clover_report($codeCoverage, $path)); - } catch (Exception $e) { - __c3_error($e->getMessage()); - } - - return __c3_exit(); - case 'crap4j': - try { - __c3_send_file(__c3_build_crap4j_report($codeCoverage, $path)); - } catch (Exception $e) { - __c3_error($e->getMessage()); - } - - return __c3_exit(); - case 'serialized': - try { - __c3_send_file($complete_report); - } catch (Exception $e) { - __c3_error($e->getMessage()); - } - - return __c3_exit(); - case 'phpunit': - try { - __c3_send_file(__c3_build_phpunit_report($codeCoverage, $path)); - } catch (Exception $e) { - __c3_error($e->getMessage()); - } - - return __c3_exit(); - } -} else { - list($codeCoverage) = __c3_factory(null); - $codeCoverage->start(C3_CODECOVERAGE_TESTNAME); - if (!array_key_exists('HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG', $_SERVER)) { - register_shutdown_function( - function () use ($codeCoverage, $current_report) { - $codeCoverage->stop(); - if (!file_exists(dirname($current_report))) { // verify directory exists - if (!mkdir(dirname($current_report), 0777, true)) { - __c3_error("Can't write CodeCoverage report into $current_report"); - } - } - // This will either lock the existing report for writing and return it along with a file pointer, - // or return a fresh PHP_CodeCoverage object without a file pointer. We'll merge the current request - // into that coverage object, write it to disk, and release the lock. By doing this in the end of - // the request, we avoid this scenario, where Request 2 overwrites the changes from Request 1: - // - // Time -> - // Request 1 [ ] - // Request 2 [ ] - // - // In addition, by locking the file for exclusive writing, we make sure no other request try to - // read/write to the file at the same time as this request (leading to a corrupt file). flock() is a - // blocking call, so it waits until an exclusive lock can be acquired before continuing. - list($existingCodeCoverage, $file) = __c3_factory($current_report, true); - $existingCodeCoverage->merge($codeCoverage); - if ($file === null) { - file_put_contents($current_report, serialize($existingCodeCoverage), LOCK_EX); - } else { - fseek($file, 0); - fwrite($file, serialize($existingCodeCoverage)); - fflush($file); - flock($file, LOCK_UN); - fclose($file); - } - } - ); - } -} -// @codeCoverageIgnoreEnd diff --git a/config/auth.php b/config/auth.php index a9264b4a2b7e..4a08ef23ac7e 100644 --- a/config/auth.php +++ b/config/auth.php @@ -66,7 +66,7 @@ return [ 'providers' => [ 'users' => [ - 'driver' => 'eloquent', + 'driver' => env('AUTH_PROVIDER', 'eloquent'), 'model' => App\User::class, ], diff --git a/config/database.php b/config/database.php index 2abd580544e4..f92c7068827b 100644 --- a/config/database.php +++ b/config/database.php @@ -81,6 +81,35 @@ return [ 'prefix_indexes' => true, ], + 'db-ninja-1' => [ + 'driver' => 'mysql', + 'host' => env('DB_HOST1', env('DB_HOST', 'localhost')), + 'database' => env('DB_DATABASE1', env('DB_DATABASE', 'forge')), + 'username' => env('DB_USERNAME1', env('DB_USERNAME', 'forge')), + 'password' => env('DB_PASSWORD1', env('DB_PASSWORD', '')), + 'port' => env('DB_PORT1', env('DB_PORT', '3306')), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => env('DB_STRICT', false), + 'engine' => 'InnoDB', + ], + + 'db-ninja-2' => [ + 'driver' => 'mysql', + 'host' => env('DB_HOST2', env('DB_HOST', 'localhost')), + 'database' => env('DB_DATABASE2', env('DB_DATABASE', 'forge')), + 'username' => env('DB_USERNAME2', env('DB_USERNAME', 'forge')), + 'password' => env('DB_PASSWORD2', env('DB_PASSWORD', '')), + 'port' => env('DB_PORT2', env('DB_PORT', '3306')), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => env('DB_STRICT', false), + 'engine' => 'InnoDB', + ], ], /* diff --git a/public/css/login.css b/public/css/login.css new file mode 100644 index 000000000000..4dd5ad6db94f --- /dev/null +++ b/public/css/login.css @@ -0,0 +1,35 @@ +html,body { + font-family: 'Open Sans', serif; + font-size: 14px; + font-weight: 300; +} +.hero.is-success { + background: #F2F6FA; +} +.hero .nav, .hero.is-success .nav { + -webkit-box-shadow: none; + box-shadow: none; +} +.box { + margin-top: 5rem; +} +.avatar { + margin-top: -70px; + padding-bottom: 20px; +} +.avatar img { + padding: 5px; + background: #fff; + border-radius: 50%; + -webkit-box-shadow: 0 2px 3px rgba(10,10,10,.1), 0 0 0 1px rgba(10,10,10,.1); + box-shadow: 0 2px 3px rgba(10,10,10,.1), 0 0 0 1px rgba(10,10,10,.1); +} +input { + font-weight: 300; +} +p { + font-weight: 700; +} +p.subtitle { + padding-top: 1rem; +} \ No newline at end of file diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php new file mode 100644 index 000000000000..44a474e48a10 --- /dev/null +++ b/resources/views/auth/login.blade.php @@ -0,0 +1,60 @@ + + + + + + + + Login - Free Bulma template + + + + + + + + +
+
+
+
+

Login

+

Please login to proceed.

+
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+ +
+ +
+
+

+ Sign Up  ·  + Forgot Password  ·  + Need Help? +

+
+
+
+
+ + + + \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index a74e26c80e77..49275879c627 100644 --- a/routes/web.php +++ b/routes/web.php @@ -14,3 +14,8 @@ Route::get('/', function () { return view('master'); }); +Route::get('/logins', function () { + return view('login.login'); +}); +Route::get('/login', ['as' => 'login', 'uses' => 'Auth\LoginController@showLoginForm']); +Route::post('/login', ['as' => 'login', 'uses' => 'Auth\LoginController@login']); diff --git a/tests/Unit/NumberTest.php b/tests/Unit/NumberTest.php index 8c9d7af29fbb..8341fa0b2037 100644 --- a/tests/Unit/NumberTest.php +++ b/tests/Unit/NumberTest.php @@ -5,12 +5,10 @@ namespace Tests\Unit; use App\Utils\NumberHelper; use Tests\TestCase; - /** - * @test - * @covers App\Utils\NumberHelper - * - */ - +/** + * @test + * @covers App\Utils\NumberHelper + */ class NumberTest extends TestCase { public function testRoundingThreeLow()