From d238d53b26a1f90bb9cb7234dcac6b78573dc2b6 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 Mar 2021 10:40:18 +1100 Subject: [PATCH 01/11] Delete PDF helper --- app/Http/Middleware/SetEmailDb.php | 8 +++++--- app/Jobs/Entity/CreateEntityPdf.php | 2 ++ app/Jobs/Util/UnlinkFile.php | 2 ++ app/Libraries/MultiDB.php | 5 +++-- app/Services/Credit/CreditService.php | 10 +++++++++- app/Services/Quote/QuoteService.php | 8 ++++++++ app/Services/Recurring/RecurringService.php | 8 ++++++++ 7 files changed, 37 insertions(+), 6 deletions(-) diff --git a/app/Http/Middleware/SetEmailDb.php b/app/Http/Middleware/SetEmailDb.php index 2ae4e7d4890b..8a977d0f30a8 100644 --- a/app/Http/Middleware/SetEmailDb.php +++ b/app/Http/Middleware/SetEmailDb.php @@ -33,10 +33,12 @@ class SetEmailDb ]; if ($request->input('email') && config('ninja.db.multi_db_enabled')) { - nlog("trying to find db"); - if (! MultiDB::userFindAndSetDb($request->input('email'))) { + + + if (! MultiDB::userFindAndSetDb($request->input('email'))) return response()->json($error, 400); - } + + } // else { // return response()->json($error, 403); diff --git a/app/Jobs/Entity/CreateEntityPdf.php b/app/Jobs/Entity/CreateEntityPdf.php index e2467e254397..5f368e5d64fa 100644 --- a/app/Jobs/Entity/CreateEntityPdf.php +++ b/app/Jobs/Entity/CreateEntityPdf.php @@ -92,6 +92,8 @@ class CreateEntityPdf implements ShouldQueue App::forgetInstance('translator'); Lang::replace(Ninja::transformTranslations($this->entity->client->getMergedSettings())); + $this->entity->service()->deletePdf(); + if (config('ninja.phantomjs_pdf_generation')) { return (new Phantom)->generate($this->invitation); } diff --git a/app/Jobs/Util/UnlinkFile.php b/app/Jobs/Util/UnlinkFile.php index a7f5f02cb4de..70461add3b70 100644 --- a/app/Jobs/Util/UnlinkFile.php +++ b/app/Jobs/Util/UnlinkFile.php @@ -39,6 +39,8 @@ class UnlinkFile implements ShouldQueue */ public function handle() { + // nlog("deleting"); + // nlog($this->file_path); Storage::disk($this->disk)->delete($this->file_path); } } diff --git a/app/Libraries/MultiDB.php b/app/Libraries/MultiDB.php index 3240283af898..dd5237f35ec9 100644 --- a/app/Libraries/MultiDB.php +++ b/app/Libraries/MultiDB.php @@ -187,9 +187,10 @@ class MultiDB //multi-db active foreach (self::$dbs as $db) { - if (User::on($db)->where(['email' => $email])->get()->count() >= 1) { // if user already exists, validation will fail + + if (User::on($db)->where(['email' => $email])->count() >= 1) return true; - } + } return false; diff --git a/app/Services/Credit/CreditService.php b/app/Services/Credit/CreditService.php index 85ab8fddc64c..8912c41e3561 100644 --- a/app/Services/Credit/CreditService.php +++ b/app/Services/Credit/CreditService.php @@ -11,6 +11,7 @@ namespace App\Services\Credit; +use App\Jobs\Util\UnlinkFile; use App\Models\Credit; use App\Utils\Traits\MakesHash; @@ -134,7 +135,14 @@ class CreditService return $this; } - + + public function deletePdf() + { + UnlinkFile::dispatchNow(config('filesystems.default'), $this->credit->client->credit_filepath() . $this->credit->number.'.pdf'); + + return $this; + } + /** * Saves the credit. * @return Credit object diff --git a/app/Services/Quote/QuoteService.php b/app/Services/Quote/QuoteService.php index fbf53e623a37..01488348d12c 100644 --- a/app/Services/Quote/QuoteService.php +++ b/app/Services/Quote/QuoteService.php @@ -12,6 +12,7 @@ namespace App\Services\Quote; use App\Events\Quote\QuoteWasApproved; +use App\Jobs\Util\UnlinkFile; use App\Models\Invoice; use App\Models\Quote; use App\Repositories\QuoteRepository; @@ -189,6 +190,13 @@ class QuoteService return $this; } + public function deletePdf() + { + UnlinkFile::dispatchNow(config('filesystems.default'), $this->quote->client->quote_filepath() . $this->quote->number.'.pdf'); + + return $this; + } + /** * Saves the quote. * @return Quote|null diff --git a/app/Services/Recurring/RecurringService.php b/app/Services/Recurring/RecurringService.php index 9942efcc69e5..00811fa404ff 100644 --- a/app/Services/Recurring/RecurringService.php +++ b/app/Services/Recurring/RecurringService.php @@ -11,6 +11,7 @@ namespace App\Services\Recurring; +use App\Jobs\Util\UnlinkFile; use App\Models\RecurringInvoice; use App\Services\Recurring\GetInvoicePdf; use Illuminate\Support\Carbon; @@ -84,6 +85,13 @@ class RecurringService return (new GetInvoicePdf($this->recurring_entity, $contact))->run(); } + public function deletePdf() + { + UnlinkFile::dispatchNow(config('filesystems.default'), $this->recurring_entity->client->recurring_invoice_filepath() . $this->recurring_entity->number.'.pdf'); + + return $this; + } + public function save() { $this->recurring_entity->save(); From 9a2160c325a8ed35b499f34ded535c3a0bdf74a5 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 Mar 2021 13:51:00 +1100 Subject: [PATCH 02/11] Verified users only for hosted platform --- app/Http/Middleware/UserVerified.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/Http/Middleware/UserVerified.php b/app/Http/Middleware/UserVerified.php index ce8a899764aa..06b45d9c4926 100644 --- a/app/Http/Middleware/UserVerified.php +++ b/app/Http/Middleware/UserVerified.php @@ -13,6 +13,7 @@ namespace App\Http\Middleware; use App\Libraries\MultiDB; use App\Models\User; +use App\Utils\Ninja; use Closure; use Hashids\Hashids; use Illuminate\Http\Request; @@ -38,16 +39,14 @@ class UserVerified */ public function handle($request, Closure $next) { + if(Ninja::isSelfHost()) + return $next($request); $error = [ 'message' => 'Email confirmation required.', 'errors' => new \stdClass, ]; - // nlog(auth()->user()->toArray()); - // nlog($this->user->toArray()); - // nlog((bool)$this->user->isVerified()); - if ($this->user && !$this->user->isVerified()) return response()->json($error, 403); From 2ab76e909886a95ffacd18ca17feccc2b82dd029 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 Mar 2021 22:00:07 +1100 Subject: [PATCH 03/11] v5.1.12 --- VERSION.txt | 2 +- app/Models/Presenters/CompanyPresenter.php | 6 +++++- config/ninja.php | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 831c6cd60031..b3fc69690c5a 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.1.11 \ No newline at end of file +5.1.12 \ No newline at end of file diff --git a/app/Models/Presenters/CompanyPresenter.php b/app/Models/Presenters/CompanyPresenter.php index 10410d9a8a0b..dde59b55649e 100644 --- a/app/Models/Presenters/CompanyPresenter.php +++ b/app/Models/Presenters/CompanyPresenter.php @@ -36,7 +36,11 @@ class CompanyPresenter extends EntityPresenter $settings = $this->entity->settings; } - return (strlen($settings->company_logo) > 0) ? url('') . $settings->company_logo : 'https://www.invoiceninja.com/wp-content/uploads/2019/01/InvoiceNinja-Logo-Round-300x300.png'; + if(strlen($settings->company_logo)) + return asset($settings->company_logo); + else + return 'https://www.invoiceninja.com/wp-content/uploads/2019/01/InvoiceNinja-Logo-Round-300x300.png'; + } public function address($settings = null) diff --git a/config/ninja.php b/config/ninja.php index 39402be58760..0662c2573a02 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -13,7 +13,7 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', ''), - 'app_version' => '5.1.11', + 'app_version' => '5.1.12', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false), From bf326e6d26db795075edf6f2768030d1e10a9b1f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 Mar 2021 23:06:24 +1100 Subject: [PATCH 04/11] Working on adding user to another company --- app/Http/Requests/User/StoreUserRequest.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/Http/Requests/User/StoreUserRequest.php b/app/Http/Requests/User/StoreUserRequest.php index 512994be8b3e..901d4b42f296 100644 --- a/app/Http/Requests/User/StoreUserRequest.php +++ b/app/Http/Requests/User/StoreUserRequest.php @@ -39,9 +39,9 @@ class StoreUserRequest extends Request $rules['last_name'] = 'required|string|max:100'; if (config('ninja.db.multi_db_enabled')) { - $rules['email'] = ['email', new ValidUserForCompany(), Rule::unique('users')->ignore($this->input('company_user.account.id'), 'account_id')]; + $rules['email'] = ['email', new ValidUserForCompany(), Rule::unique('users')->ignore(auth()->user()->company()->account_id, 'account_id')]; } else { - $rules['email'] = ['email',Rule::unique('users')->ignore($this->input('company_user.account.id'), 'account_id')]; + $rules['email'] = ['email',Rule::unique('users')->ignore(auth()->user()->company()->account_id, 'account_id')]; } @@ -56,7 +56,10 @@ class StoreUserRequest extends Request { $input = $this->all(); -nlog($this->input('company_user.account')); +//unique user rule - check company_user table for user_id / company_id / account_id if none exist we can add the user. ELSE return false + +//nlog($this->all()); +//nlog($this->input('company_user.account')); // nlog($this->input('company_user.account.id')); // nlog($this->input('company_user.account.id')); From ab5fb7c37609c133e1a8ebd7d78c816db8614fce Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 Mar 2021 07:15:28 +1100 Subject: [PATCH 05/11] Fixes for company logo --- app/Models/Presenters/CompanyPresenter.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/Models/Presenters/CompanyPresenter.php b/app/Models/Presenters/CompanyPresenter.php index dde59b55649e..247cce90d47d 100644 --- a/app/Models/Presenters/CompanyPresenter.php +++ b/app/Models/Presenters/CompanyPresenter.php @@ -36,8 +36,10 @@ class CompanyPresenter extends EntityPresenter $settings = $this->entity->settings; } - if(strlen($settings->company_logo)) - return asset($settings->company_logo); + if(strlen($settings->company_logo) >= 1 && strpos($settings->company_logo, 'http')) + return $settings->company_logo; + else if(strlen($settings->company_logo) >= 1) + return url('') . $settings->company_logo; else return 'https://www.invoiceninja.com/wp-content/uploads/2019/01/InvoiceNinja-Logo-Round-300x300.png'; From d085e1b0895ca4d5966e4e63af357bb6c6259618 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 Mar 2021 07:15:51 +1100 Subject: [PATCH 06/11] v5.1.13 --- VERSION.txt | 2 +- config/ninja.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index b3fc69690c5a..0c5d745ebb0d 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.1.12 \ No newline at end of file +5.1.13 \ No newline at end of file diff --git a/config/ninja.php b/config/ninja.php index 0662c2573a02..40e4e421927e 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -13,7 +13,7 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', ''), - 'app_version' => '5.1.12', + 'app_version' => '5.1.13', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false), From edc25c13427a5a775d073ebc1a73741ce10cfeac Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 Mar 2021 09:08:57 +1100 Subject: [PATCH 07/11] Associate a user with multiple companies --- app/Http/Requests/User/StoreUserRequest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Http/Requests/User/StoreUserRequest.php b/app/Http/Requests/User/StoreUserRequest.php index 901d4b42f296..d61b46e59726 100644 --- a/app/Http/Requests/User/StoreUserRequest.php +++ b/app/Http/Requests/User/StoreUserRequest.php @@ -14,6 +14,7 @@ namespace App\Http\Requests\User; use App\DataMapper\DefaultSettings; use App\Factory\UserFactory; use App\Http\Requests\Request; +use App\Http\ValidationRules\User\AttachableUser; use App\Http\ValidationRules\ValidUserForCompany; use App\Libraries\MultiDB; use App\Models\User; @@ -39,9 +40,9 @@ class StoreUserRequest extends Request $rules['last_name'] = 'required|string|max:100'; if (config('ninja.db.multi_db_enabled')) { - $rules['email'] = ['email', new ValidUserForCompany(), Rule::unique('users')->ignore(auth()->user()->company()->account_id, 'account_id')]; + $rules['email'] = ['email', new ValidUserForCompany(), new AttachableUser()]; } else { - $rules['email'] = ['email',Rule::unique('users')->ignore(auth()->user()->company()->account_id, 'account_id')]; + $rules['email'] = ['email', new AttachableUser()]; } From c37c441d923a52f128f038c6efbbdd6ac133c649 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 Mar 2021 09:18:19 +1100 Subject: [PATCH 08/11] Associate a user with multiple companies --- .../ValidationRules/User/AttachableUser.php | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 app/Http/ValidationRules/User/AttachableUser.php diff --git a/app/Http/ValidationRules/User/AttachableUser.php b/app/Http/ValidationRules/User/AttachableUser.php new file mode 100644 index 000000000000..6c572cc32f78 --- /dev/null +++ b/app/Http/ValidationRules/User/AttachableUser.php @@ -0,0 +1,73 @@ +checkUserIsAttachable($value); + } + + /** + * @return string + */ + public function message() + { + return "Cannot add the same user to the same company"; + } + + /** + * @param $user_id + * @return bool + */ + private function checkUserIsAttachable($email) : bool + { + if (empty($email)) { + return false; + } + + $user = User::where('email', $email)->first(); + + if(!$user) + return true; + + $user_already_attached = CompanyUser::query() + ->where('user_id', $user->id) + ->where('account_id',$user->account_id) + ->where('company_id', auth()->user()->company()->id) + ->withTrashed() + ->exists(); + + if($user_already_attached) + return false; + + + return true; + } +} From 18fa5377916fb9efa0da9e3120197f7d39a43ecb Mon Sep 17 00:00:00 2001 From: = Date: Tue, 2 Mar 2021 18:52:25 +1100 Subject: [PATCH 09/11] Add one time token functionality --- .../Controllers/OneTimeTokenController.php | 77 +++++++++++++++++++ .../OneTimeToken/OneTimeTokenRequest.php | 45 +++++++++++ routes/api.php | 2 + 3 files changed, 124 insertions(+) create mode 100644 app/Http/Controllers/OneTimeTokenController.php create mode 100644 app/Http/Requests/OneTimeToken/OneTimeTokenRequest.php diff --git a/app/Http/Controllers/OneTimeTokenController.php b/app/Http/Controllers/OneTimeTokenController.php new file mode 100644 index 000000000000..efe83ccd8af7 --- /dev/null +++ b/app/Http/Controllers/OneTimeTokenController.php @@ -0,0 +1,77 @@ + auth()->user()->id, + 'company_key'=> auth()->company()->company_key, + 'context' => $requst->input('context'), + ]; + + Cache::put( $hash, $data, 3600 ); + + return response()->json(['hash' => $hash], 200); + + } +} diff --git a/app/Http/Requests/OneTimeToken/OneTimeTokenRequest.php b/app/Http/Requests/OneTimeToken/OneTimeTokenRequest.php new file mode 100644 index 000000000000..a5bee734c2cb --- /dev/null +++ b/app/Http/Requests/OneTimeToken/OneTimeTokenRequest.php @@ -0,0 +1,45 @@ + 'required', + ]; + } + + protected function prepareForValidation() + { + // $input = $this->all(); + // $this->replace($input); + } +} diff --git a/routes/api.php b/routes/api.php index 5728607a20c1..0c71a77c6b27 100644 --- a/routes/api.php +++ b/routes/api.php @@ -93,6 +93,8 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::post('migration/purge_save_settings/{company}', 'MigrationController@purgeCompanySaveSettings')->middleware('password_protected'); Route::post('migration/start', 'MigrationController@startMigration'); + Route::post('one_time_token', 'OneTimeTokenController@create'); + Route::resource('payments', 'PaymentController'); // name = (payments. index / create / show / update / destroy / edit Route::post('payments/refund', 'PaymentController@refund')->name('payments.refund'); Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk'); From aa290172ae20a080f7aee9f40134db827c7143ea Mon Sep 17 00:00:00 2001 From: = Date: Tue, 2 Mar 2021 19:03:01 +1100 Subject: [PATCH 10/11] One Time Token Requests --- .../Controllers/OneTimeTokenController.php | 27 ++++++++++- .../OneTimeToken/OneTimeRouterRequest.php | 45 +++++++++++++++++++ routes/api.php | 1 + 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 app/Http/Requests/OneTimeToken/OneTimeRouterRequest.php diff --git a/app/Http/Controllers/OneTimeTokenController.php b/app/Http/Controllers/OneTimeTokenController.php index efe83ccd8af7..e735f39b1c19 100644 --- a/app/Http/Controllers/OneTimeTokenController.php +++ b/app/Http/Controllers/OneTimeTokenController.php @@ -11,14 +11,16 @@ namespace App\Http\Controllers; +use App\Http\Requests\OneTimeToken\OneTimeRouterRequest; use App\Http\Requests\OneTimeToken\OneTimeTokenRequest; +use App\Models\User; use Illuminate\Http\Response; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Str; class OneTimeTokenController extends BaseController { - use DispatchesJobs; public function __construct() { @@ -74,4 +76,27 @@ class OneTimeTokenController extends BaseController return response()->json(['hash' => $hash], 200); } + + public function router(OneTimeRouterRequest $request) + { + $data = Cache::get($request->input('hash')); + + MultiDB::findAndSetDbByCompanyKey($data['company_key']); + + $user = User::findOrFail($data['user_id']); + + Auth::login($user, true); + + Cache::forget($request->input('hash')); + + $this->sendTo($data['context']); + + } + + /* We need to merge all contexts here and redirect to the correct location */ + private function sendTo($context) + { + + return redirect(); + } } diff --git a/app/Http/Requests/OneTimeToken/OneTimeRouterRequest.php b/app/Http/Requests/OneTimeToken/OneTimeRouterRequest.php new file mode 100644 index 000000000000..93bb35ad3a69 --- /dev/null +++ b/app/Http/Requests/OneTimeToken/OneTimeRouterRequest.php @@ -0,0 +1,45 @@ + 'required', + ]; + } + + protected function prepareForValidation() + { + // $input = $this->all(); + // $this->replace($input); + } +} diff --git a/routes/api.php b/routes/api.php index 0c71a77c6b27..4348c24b679d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -180,5 +180,6 @@ Route::match(['get', 'post'], 'payment_webhook/{company_key}/{company_gateway_id ->name('payment_webhook'); Route::post('api/v1/postmark_webhook', 'PostMarkController@webhook'); +Route::get('token_hash_router', 'OneTimeTokenController@router'); Route::fallback('BaseController@notFound'); From 7a2cea1d9c6e6fa35e3330486f3f3d9edb50525f Mon Sep 17 00:00:00 2001 From: = Date: Tue, 2 Mar 2021 19:54:23 +1100 Subject: [PATCH 11/11] One Time Token --- app/Http/Controllers/OneTimeTokenController.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/Http/Controllers/OneTimeTokenController.php b/app/Http/Controllers/OneTimeTokenController.php index e735f39b1c19..17d1a7da7ed3 100644 --- a/app/Http/Controllers/OneTimeTokenController.php +++ b/app/Http/Controllers/OneTimeTokenController.php @@ -22,6 +22,12 @@ use Illuminate\Support\Str; class OneTimeTokenController extends BaseController { + private $contexts = [ + 'stripe_connect_test' => 'https://connect.stripe.com/oauth/authorize?response_type=code&client_id=ca_J2FhIhcf9GT5BlWUNeQ1FhnZACaYZrOI&scope=read_write +', + 'stripe_connect' => 'https://connect.stripe.com/oauth/authorize?response_type=code&client_id=ca_J2Fh2tZfMlaaItUfbUwBBx4JPss8jCz9&scope=read_write' + ]; + public function __construct() { parent::__construct();