mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-03 19:37:32 -05:00 
			
		
		
		
	Multi DB Authentication (#2431)
* Custom Multi DB User Provider * Multi DB Authentication provider * Finalized Multi Auth DB * Apply fixes from StyleCI (#22)
This commit is contained in:
		
							parent
							
								
									8d5c5b1257
								
							
						
					
					
						commit
						85180bfdb7
					
				@ -37,3 +37,5 @@ PUSHER_APP_CLUSTER=mt1
 | 
			
		||||
 | 
			
		||||
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
 | 
			
		||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
 | 
			
		||||
 | 
			
		||||
AUTH_PROVIDER=
 | 
			
		||||
 | 
			
		||||
@ -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');
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										227
									
								
								app/Providers/MultiDatabaseUserProvider.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								app/Providers/MultiDatabaseUserProvider.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,227 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Providers;
 | 
			
		||||
 | 
			
		||||
use App\User;
 | 
			
		||||
use Illuminate\Auth\GenericUser;
 | 
			
		||||
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
 | 
			
		||||
use Illuminate\Contracts\Auth\UserProvider;
 | 
			
		||||
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
 | 
			
		||||
use Illuminate\Database\ConnectionInterface;
 | 
			
		||||
use Illuminate\Support\Facades\App;
 | 
			
		||||
use Illuminate\Support\Facades\Config;
 | 
			
		||||
use Illuminate\Support\Facades\DB;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
use Illuminate\Support\Str;
 | 
			
		||||
 | 
			
		||||
class MultiDatabaseUserProvider implements UserProvider
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * The active database connection.
 | 
			
		||||
     *
 | 
			
		||||
     * @var \Illuminate\Database\ConnectionInterface
 | 
			
		||||
     */
 | 
			
		||||
    protected $conn;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The hasher implementation.
 | 
			
		||||
     *
 | 
			
		||||
     * @var \Illuminate\Contracts\Hashing\Hasher
 | 
			
		||||
     */
 | 
			
		||||
    protected $hasher;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The table containing the users.
 | 
			
		||||
     *
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    protected $table;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new database user provider.
 | 
			
		||||
     *
 | 
			
		||||
     * @param \Illuminate\Contracts\Hashing\Hasher $hasher
 | 
			
		||||
     * @param string                               $table
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct(ConnectionInterface $conn, HasherContract $hasher, $table = 'users')
 | 
			
		||||
    {
 | 
			
		||||
        $this->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));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										336
									
								
								c3.php
									
									
									
									
									
								
							
							
						
						
									
										336
									
								
								c3.php
									
									
									
									
									
								
							@ -1,336 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
// @codingStandardsIgnoreFile
 | 
			
		||||
// @codeCoverageIgnoreStart
 | 
			
		||||
/**
 | 
			
		||||
 * C3 - Codeception Code Coverage.
 | 
			
		||||
 *
 | 
			
		||||
 * @author tiger
 | 
			
		||||
 */
 | 
			
		||||
// $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG'] = 1;
 | 
			
		||||
if (isset($_COOKIE['CODECEPTION_CODECOVERAGE'])) {
 | 
			
		||||
    $cookie = json_decode($_COOKIE['CODECEPTION_CODECOVERAGE'], true);
 | 
			
		||||
    // fix for improperly encoded JSON in Code Coverage cookie with WebDriver.
 | 
			
		||||
    // @see https://github.com/Codeception/Codeception/issues/874
 | 
			
		||||
    if (!is_array($cookie)) {
 | 
			
		||||
        $cookie = json_decode($cookie, true);
 | 
			
		||||
    }
 | 
			
		||||
    if ($cookie) {
 | 
			
		||||
        foreach ($cookie as $key => $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 [ <read>               <write>          ]
 | 
			
		||||
                // Request 2 [         <read>                <write> ]
 | 
			
		||||
                //
 | 
			
		||||
                // 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
 | 
			
		||||
@ -66,7 +66,7 @@ return [
 | 
			
		||||
 | 
			
		||||
    'providers' => [
 | 
			
		||||
        'users' => [
 | 
			
		||||
            'driver' => 'eloquent',
 | 
			
		||||
            'driver' => env('AUTH_PROVIDER', 'eloquent'),
 | 
			
		||||
            'model'  => App\User::class,
 | 
			
		||||
        ],
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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',
 | 
			
		||||
        ],
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										35
									
								
								public/css/login.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								public/css/login.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										60
									
								
								resources/views/auth/login.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								resources/views/auth/login.blade.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8">
 | 
			
		||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
			
		||||
    <title>Login - Free Bulma template</title>
 | 
			
		||||
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
 | 
			
		||||
    <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,700" rel="stylesheet">
 | 
			
		||||
    <!-- Bulma Version 0.7.1-->
 | 
			
		||||
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="css/login.css">
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
    <section class="hero is-success is-fullheight">
 | 
			
		||||
        <div class="hero-body">
 | 
			
		||||
            <div class="container has-text-centered">
 | 
			
		||||
                <div class="column is-4 is-offset-4">
 | 
			
		||||
                    <h3 class="title has-text-grey">Login</h3>
 | 
			
		||||
                    <p class="subtitle has-text-grey">Please login to proceed.</p>
 | 
			
		||||
                    <div class="box">
 | 
			
		||||
                        <figure class="avatar">
 | 
			
		||||
                            <img src="https://placehold.it/128x128">
 | 
			
		||||
                        </figure>
 | 
			
		||||
                        <form>
 | 
			
		||||
                            <div class="field">
 | 
			
		||||
                                <div class="control">
 | 
			
		||||
                                    <input class="input is-large" type="email" placeholder="Your Email" autofocus="">
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                            <div class="field">
 | 
			
		||||
                                <div class="control">
 | 
			
		||||
                                    <input class="input is-large" type="password" placeholder="Your Password">
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="field">
 | 
			
		||||
                                <label class="checkbox">
 | 
			
		||||
                  <input type="checkbox">
 | 
			
		||||
                  Remember me
 | 
			
		||||
                </label>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <button class="button is-block is-info is-large is-fullwidth">Login</button>
 | 
			
		||||
                        </form>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <p class="has-text-grey">
 | 
			
		||||
                        <a href="../">Sign Up</a>  · 
 | 
			
		||||
                        <a href="../">Forgot Password</a>  · 
 | 
			
		||||
                        <a href="../">Need Help?</a>
 | 
			
		||||
                    </p>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </section>
 | 
			
		||||
    <script async type="text/javascript" src="../js/bulma.js"></script>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
@ -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']);
 | 
			
		||||
 | 
			
		||||
@ -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()
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user