1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-09 20:52:56 +01:00

Refactor constants, set active db connection in middleware, add socket.io dependencies (#2463)

This commit is contained in:
David Bomba 2018-10-21 09:26:21 +11:00 committed by GitHub
parent 103a95955a
commit 50e22ee1d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 303 additions and 30 deletions

View File

@ -28,4 +28,6 @@ MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_ENCRYPTION=null
POSTMARK_API_TOKEN=

View File

@ -25,7 +25,7 @@ if (! defined('APP_NAME')) {
define('TEST_PASSWORD', 'password');
define('BANK_LIBRARY_OFX', 1);
define('MULTI_DBS', serialize(['db-ninja-1', 'db-ninja-2']));
//define('MULTI_DBS', serialize(['db-ninja-1', 'db-ninja-2']));
define('RANDOM_KEY_LENGTH', 32); //63340286662973277706162286946811886609896461828096 combinations
define('SOCIAL_GOOGLE', 'Google');

View File

@ -0,0 +1,38 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class UserSignedUp
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $user;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($user)
{
$this->user = $user;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@ -2,10 +2,14 @@
namespace App\Http\Controllers;
use App\Events\UserSignedUp;
use App\Http\Requests\SignupRequest;
use App\Jobs\Account\AccountCreated;
use App\Models\Account;
use App\Models\User;
use App\Models\UserAccount;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
/**
@ -15,6 +19,9 @@ use Illuminate\Support\Facades\Hash;
class SignupController extends Controller
{
use DispatchesJobs;
/**
* SignupController constructor.
*/
@ -39,7 +46,6 @@ class SignupController extends Controller
//dd($request->validated());
//created new account
$ac = new Account();
$ac->name = $request->first_name. ' ' .$request->last_name;
$ac->account_key = strtolower(str_random(RANDOM_KEY_LENGTH));
@ -49,6 +55,7 @@ class SignupController extends Controller
$user = new User();
$user->password = Hash::make($request->input('password'));
$user->accepted_terms_version = NINJA_TERMS_VERSION;
$user->confirmation_code = strtolower(str_random(RANDOM_KEY_LENGTH));
$user->db = config('database.default');
$user->fill($request->all());
$user->save();
@ -63,12 +70,15 @@ class SignupController extends Controller
$user_account->permissions = '';
$user_account->save();
dd($user);
//log user in
Auth::guard('user')->login($user, true);
//fire account created job
event(new UserSignedUp($user));
//redirect to localization setup workflow
return redirect()->route('user.dashboard');
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function confirm($code)
{
$user = User::where('confirmation_code', '=', $code)->get()->first();
if ($user) {
$user->email_verified_at = now();
$user->confirmation_code = null;
$user->save();
redirect('user.dashboard')->with('message', trans('texts.security_confirmation'));
} else {
return Redirect::to('/login')->with('error', trans('texts.wrong_confirmation'));
}
}
}

View File

@ -25,7 +25,7 @@ class RedirectIfAuthenticated
break;
case 'user':
if (Auth::guard($guard)->check()) {
return redirect('user.dashboard');
return redirect()->route('user.dashboard');
}
break;
default:

View File

@ -16,9 +16,11 @@ class SetDb
*/
public function handle($request, Closure $next)
{
if (! config('auth.providers.users.driver') == 'eloquent')
if (config('ninja.db.multi_db_enabled'))
{
MultiDB::setDB(auth()->user()->db);
}
return $next($request);

View File

@ -11,19 +11,32 @@ use App\Models\User;
class MultiDB
{
const DB_NINJA_1 = 1;
const DB_NINJA_2 = 2;
public static $dbs = ['db-ninja-1', 'db-ninja-2'];
/**
* @param $email
* @return bool
*/
public static function getDbs()
{
return self::$dbs;
}
public static function checkUserEmailExists($email) : bool
{
if (config('auth.providers.users.driver') == 'eloquent') //default eloquent = single DB
if (! config('ninja.db.multi_db_enabled'))
{
return User::where(['email' => $email])->get()->count() >= 1 ?? false; // true >= 1 emails found / false -> == emails found
}
//multi-db active
foreach (unserialize(MULTI_DBS) as $db)
foreach (self::$dbs as $db)
{
if(User::on($db)->where(['email' => $email])->get()->count() >=1) // if user already exists, validation will fail
return true;
@ -39,19 +52,23 @@ class MultiDB
*/
public static function hasUser(array $data)
{
if (config('auth.providers.users.driver') == 'eloquent') //default eloquent = single DB
if (! config('ninja.db.multi_db_enabled'))
{
return User::where($data)->first();
}
foreach (unserialize(MULTI_DBS) as $db)
foreach (self::$dbs as $db)
{
self::setDB($db);
$user = User::where($data)->first();
if($user)
if($user) {
return $user;
}
}
return false;

View File

@ -0,0 +1,30 @@
<?php
namespace App\Listeners;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendConfirmationNotification
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle($event)
{
//send confirmation email using $event->user
}
}

View File

@ -2,6 +2,7 @@
namespace App\Models;
use App\Models\Traits\UserTrait;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
@ -11,6 +12,7 @@ class User extends Authenticatable
{
use Notifiable;
use SoftDeletes;
use UserTrait;
protected $guard = 'user';

View File

@ -2,10 +2,11 @@
namespace App\Providers;
use Illuminate\Support\Facades\Event;
use App\Listeners\SendConfirmationNotification;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use App\Events\UserSignedUp;
class EventServiceProvider extends ServiceProvider
{
@ -18,6 +19,18 @@ class EventServiceProvider extends ServiceProvider
Registered::class => [
SendEmailVerificationNotification::class,
],
UserSignedUp::class => [
SendConfirmationNotification::class,
]
];
/**
* The subscriber classes to register.
*
* @var array
*/
protected $subscribe = [
];
/**

View File

@ -2,11 +2,13 @@
namespace App\Providers;
use App\Libraries\MultiDB;
use Illuminate\Support\Str;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use PhpParser\Node\Expr\BinaryOp\Mul;
class MultiDatabaseUserProvider implements UserProvider
{
@ -208,9 +210,8 @@ class MultiDatabaseUserProvider implements UserProvider
private function setDefaultDatabase($id = false, $email = false, $token = false) : void
{
$databases = unserialize(MULTI_DBS);
foreach ($databases as $database) {
foreach (MultiDB::getDbs() as $database) {
$this->setDB($database);
$query = $this->conn->table('users');

View File

@ -0,0 +1,31 @@
<?php
namespace App\Providers;
use App\Providers\UserSignedUp;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendConfirmationNotification
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param UserSignedUp $event
* @return void
*/
public function handle(UserSignedUp $event)
{
//
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Providers;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class UserSignedUp
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@ -28,7 +28,8 @@
"nwidart/laravel-modules": "^4.0",
"predis/predis": "^1.1",
"spatie/laravel-html": "^2.19",
"webpatser/laravel-countries": "dev-master#75992ad"
"webpatser/laravel-countries": "dev-master#75992ad",
"wildbit/postmark-php": "^2.6"
},
"require-dev": {
"beyondcode/laravel-dump-server": "^1.0",

36
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b059d33c67d5ecfe4b761dbc55f4e326",
"content-hash": "c51e927ba0169fada853f1d0763470ed",
"packages": [
{
"name": "asgrim/ofxparser",
@ -3070,6 +3070,40 @@
"laravel"
],
"time": "2018-06-27T11:58:05+00:00"
},
{
"name": "wildbit/postmark-php",
"version": "2.6.0",
"source": {
"type": "git",
"url": "https://github.com/wildbit/postmark-php.git",
"reference": "70162fe99ac0a80e599f76ae84e4b6e655081e04"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/wildbit/postmark-php/zipball/70162fe99ac0a80e599f76ae84e4b6e655081e04",
"reference": "70162fe99ac0a80e599f76ae84e4b6e655081e04",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "~6.0",
"php": ">=5.5.0"
},
"require-dev": {
"phpunit/phpunit": "4.4.0"
},
"type": "library",
"autoload": {
"psr-0": {
"Postmark\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "The officially supported client for Postmark (http://postmarkapp.com)",
"time": "2018-07-24T22:40:23+00:00"
}
],
"packages-dev": [

View File

@ -18,4 +18,9 @@ return [
'hosted' => env('PRIVACY_POLICY_URL', 'https://www.invoiceninja.com/privacy-policy/'),
'selfhost' => env('PRIVACY_POLICY_URL', 'https://www.invoiceninja.com/self-hosting-privacy-data-control/'),
],
'db' => [
'multi_db_enabled' => env('MULTI_DB_ENABLED', false),
'default' => env('DB_CONNECTION', 'mysql'),
]
];

View File

@ -34,40 +34,46 @@ return [
'secret' => env('SPARKPOST_SECRET'),
],
'postmark' => env('POSTMARK_API_TOKEN', ''),
'postmark_ticket' => env('POSTMARK_API_TICKET_TOKEN'),
'postmark_ticket_2' => env('POSTMARK_API_TICKET_TOKEN_2'),
'stripe' => [
'model' => App\Models\User::class,
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
],
'stripe' => [
'model' => 'User',
'secret' => '',
],
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'redirect' => env('GITHUB_OAUTH_REDIRECT'),
],
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => env('GOOGLE_OAUTH_REDIRECT'),
],
'facebook' => [
'client_id' => env('FACEBOOK_CLIENT_ID'),
'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
'redirect' => env('FACEBOOK_OAUTH_REDIRECT'),
],
'linkedin' => [
'client_id' => env('LINKEDIN_CLIENT_ID'),
'client_secret' => env('LINKEDIN_CLIENT_SECRET'),
'redirect' => env('LINKEDIN_OAUTH_REDIRECT'),
],
'twitter' => [
'client_id' => env('TWITTER_CLIENT_ID'),
'client_secret' => env('TWITTER_CLIENT_SECRET'),
'redirect' => env('TWITTER_OAUTH_REDIRECT'),
],
'bitbucket' => [
'client_id' => env('BITBUCKET_CLIENT_ID'),
'client_secret' => env('BITBUCKET_CLIENT_SECRET'),

View File

@ -27,6 +27,7 @@ class UsersTableSeeder extends Seeder
$account = Account::create([
'name' => $faker->name(),
'account_key' => strtolower(str_random(RANDOM_KEY_LENGTH)),
]);
$user = User::create([
@ -34,8 +35,7 @@ class UsersTableSeeder extends Seeder
'last_name' => $faker->lastName,
'email' => TEST_USERNAME,
'password' => Hash::make(TEST_PASSWORD),
'registered' => true,
'confirmed' => true,
'email_verified_at' => now(),
]);
$client = Client::create([
@ -49,8 +49,7 @@ class UsersTableSeeder extends Seeder
'email' => TEST_CLIENTNAME,
'account_id' => $account->id,
'password' => Hash::make(TEST_PASSWORD),
'registered' => true,
'confirmed' => true,
'email_verified_at' => now(),
'client_id' =>$client->id,
]);

View File

@ -28,5 +28,8 @@
"simple-line-icons": "2.4.1",
"vue": "^2.5.17"
},
"dependencies": {}
"dependencies": {
"laravel-echo": "^1.4.0",
"socket.io-client": "^2.1.1"
}
}

2
public/js/ninja.js vendored

File diff suppressed because one or more lines are too long

View File

@ -79,7 +79,7 @@
<li class="nav-item dropdown">
<a class="nav-link" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">
<img class="img-avatar" src="images/logo.png" alt="admin@bootstrapmaster.com"> David Bomba
<img class="img-avatar" src="images/logo.png" alt="admin@bootstrapmaster.com"> {{ auth()->user()->getDisplayName() }}
</a>
<div class="dropdown-menu dropdown-menu-right">
<!-- if multiple accounts exist, loop through here and display

View File

@ -3,32 +3,41 @@
/*
* Authentication Routes Laravel Defaults... replaces //Auth::routes();
*/
Route::get('login', 'Auth\LoginController@showLoginForm')->name('login');
Route::post('login', 'Auth\LoginController@login');
Route::post('logout', 'Auth\LoginController@logout')->name('logout');
/*
* Signup Routes
*/
Route::redirect('/', '/login', 301);
Route::get('signup', 'SignupController@signup')->name('signup');
Route::post('process_signup', 'SignupController@processSignup')->name('signup.submit');
Route::get('contact/login', 'Auth\ContactLoginController@showLoginForm')->name('contact.login');
Route::post('contact/login', 'Auth\ContactLoginController@login')->name('contact.login.submit');
/*
* Password Reset Routes...
*/
Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
Route::post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update');
/*
* Social authentication
*/
Route::get('auth/{provider}', 'Auth\LoginController@redirectToProvider');
Route::get('auth/{provider}/callback', 'Auth\LoginController@handleProviderCallback');
/*
* Authenticated User Routes
*/
Route::group(['middleware' => ['auth:user', 'db']], function () {
Route::get('dashboard', 'HomeController@user')->name('user.dashboard');
@ -37,16 +46,20 @@ Route::group(['middleware' => ['auth:user', 'db']], function () {
Route::get('settings', 'SettingsController@index')->name('user.settings');
});
/*
* Inbound routes requiring DB Lookup
*/
Route::get('/user/confirm/{confirmation_code}', 'UserController@confirm');
/*
Authenticated Contact Routes
*/
Route::group(['prefix' => 'contact', 'middleware' => 'auth:contact'], function () {
Route::get('/', 'ContactController@index')->name('contact.dashboard');
Route::get('logout', 'Auth\ContactLoginController@logout')->name('contact.logout');
});
});

View File

@ -26,7 +26,7 @@ class MultiDBUserTest extends TestCase
{
parent::setUp();
if (config('auth.providers.users.driver') == 'eloquent')
if (! config('ninja.db.multi_db_enabled'))
$this->markTestSkipped('Multi DB not enabled - skipping');
User::unguard();

View File

@ -25,7 +25,7 @@ class UniqueEmailTest extends TestCase
{
parent::setUp();
if (config('auth.providers.users.driver') == 'eloquent')
if (! config('ninja.db.multi_db_enabled'))
$this->markTestSkipped('Multi DB not enabled - skipping');
DB::connection('db-ninja-1')->table('users')->delete();