From 9cce72d0d54d571d8483077a31ee79ab884645da Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 21 Dec 2023 17:47:35 +1100 Subject: [PATCH 1/6] Updates for SMS verification --- app/Http/Controllers/TwilioController.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/Http/Controllers/TwilioController.php b/app/Http/Controllers/TwilioController.php index ed6044995b12..61cff61ff31c 100644 --- a/app/Http/Controllers/TwilioController.php +++ b/app/Http/Controllers/TwilioController.php @@ -42,6 +42,10 @@ class TwilioController extends BaseController /** @var \App\Models\User $user */ $user = auth()->user(); + if(!$user->email_verified_at) { + return response()->json(['message' => 'Please verify your email address before verifying your phone number'], 400); + } + $account = $user->company()->account; if(!$this->checkPhoneValidity($request->phone)) { @@ -146,6 +150,10 @@ class TwilioController extends BaseController return response()->json(['message' => 'Unable to retrieve user.'], 400); } + if(!$user->email_verified_at) { + return response()->json(['message' => 'Please verify your email address before verifying your phone number'], 400); + } + if (!$user->phone || $user->phone == '') { return response()->json(['message' => 'User found, but no valid phone number on file, please contact support.'], 400); } From 6d2709e6718f7421298ce466aeb8c39d62124678 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 23 Dec 2023 07:22:29 +1100 Subject: [PATCH 2/6] Updates for HTML view --- app/Http/Livewire/PdfSlot.php | 9 +++++---- .../portal/ninja2020/components/html-viewer.blade.php | 6 ++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/Http/Livewire/PdfSlot.php b/app/Http/Livewire/PdfSlot.php index 707903bd204a..28a6b3fd1407 100644 --- a/app/Http/Livewire/PdfSlot.php +++ b/app/Http/Livewire/PdfSlot.php @@ -203,20 +203,20 @@ class PdfSlot extends Component if($this->entity_type == 'invoice' || $this->entity_type == 'recurring_invoice') { foreach($this->settings->pdf_variables->invoice_details as $variable) { - $entity_details .= "

{$variable}_label

{$variable}

"; + $entity_details .= "

{$variable}_label

{$variable}

"; } } elseif($this->entity_type == 'quote') { foreach($this->settings->pdf_variables->quote_details ?? [] as $variable) { - $entity_details .= "

{$variable}_label

{$variable}

"; + $entity_details .= "

{$variable}_label

{$variable}

"; } } elseif($this->entity_type == 'credit') { foreach($this->settings->pdf_variables->credit_details ?? [] as $variable) { - $entity_details .= "

{$variable}_label

{$variable}

"; + $entity_details .= "

{$variable}_label

{$variable}

"; } } elseif($this->entity_type == 'purchase_order') { foreach($this->settings->pdf_variables->purchase_order_details ?? [] as $variable) { - $entity_details .= "

{$variable}_label

{$variable}

"; + $entity_details .= "

{$variable}_label

{$variable}

"; } } @@ -310,6 +310,7 @@ class PdfSlot extends Component return 'purchase_order'; } + return ''; } } diff --git a/resources/views/portal/ninja2020/components/html-viewer.blade.php b/resources/views/portal/ninja2020/components/html-viewer.blade.php index ab54e246d5ea..37ea0aac6685 100644 --- a/resources/views/portal/ninja2020/components/html-viewer.blade.php +++ b/resources/views/portal/ninja2020/components/html-viewer.blade.php @@ -77,6 +77,8 @@ span {
+ +

{!! $product['notes'] !!}

@if($show_quantity) {{ $product['quantity'] }} x @@ -85,8 +87,8 @@ span { @if($show_cost) {{ $product['cost'] }} @endif -

-

{!! $product['notes'] !!}

+

+
From c65cfcb31be86dbcf8a55018dd3cc526e9ae92e6 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 23 Dec 2023 07:40:39 +1100 Subject: [PATCH 3/6] Updates for importing Sepa Debit payment methods --- .../Stripe/UpdatePaymentMethods.php | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/app/PaymentDrivers/Stripe/UpdatePaymentMethods.php b/app/PaymentDrivers/Stripe/UpdatePaymentMethods.php index 5b7239f50fca..27e9c9a04358 100644 --- a/app/PaymentDrivers/Stripe/UpdatePaymentMethods.php +++ b/app/PaymentDrivers/Stripe/UpdatePaymentMethods.php @@ -73,6 +73,18 @@ class UpdatePaymentMethods $this->addOrUpdateCard($method, $customer->id, $client, GatewayType::SOFORT); } + $sepa_methods = PaymentMethod::all( + [ + 'customer' => $customer->id, + 'type' => 'sepa_debit', + ], + $this->stripe->stripe_connect_auth + ); + + foreach ($sepa_methods as $method) { + $this->addOrUpdateCard($method, $customer->id, $client, GatewayType::SEPA); + } + $this->importBankAccounts($customer, $client); $this->importPMBankAccounts($customer, $client); @@ -189,7 +201,7 @@ class UpdatePaymentMethods } /* Ignore Expired cards */ - if ($method->card->exp_year <= date('Y') && $method->card->exp_month < date('m')) { + if ($method->card && $method->card->exp_year <= date('Y') && $method->card->exp_month < date('m')) { return; } @@ -231,6 +243,15 @@ class UpdatePaymentMethods return new \stdClass; + case GatewayType::SEPA: + + $payment_meta = new \stdClass; + $payment_meta->brand = (string) \sprintf('%s (%s)', $method->sepa_debit->bank_code, ctrans('texts.sepa')); + $payment_meta->last4 = (string) $method->sepa_debit->last4; + $payment_meta->state = 'authorized'; + $payment_meta->type = GatewayType::SEPA; + + return $payment_meta; default: break; From f9e709af5ba54ef1d223f5f1f02e04004aaf09a3 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 23 Dec 2023 13:10:15 +1100 Subject: [PATCH 4/6] Add turnstile functionality --- app/Http/Controllers/AccountController.php | 19 +++++++++++++++++++ app/Http/Controllers/TwilioController.php | 8 ++++++++ config/ninja.php | 5 +++++ routes/api.php | 6 +++--- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 52a2d8532962..ce9e4918713f 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -65,6 +65,25 @@ class AccountController extends BaseController */ public function store(CreateAccountRequest $request) { + + if(config('ninja.cloudflare.turnstile.secret')) { + $r = \Illuminate\Support\Facades\Http::post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [ + 'secret' => config('ninja.cloudflare.turnstile.secret'), + 'response' => $request->input('cf-turnstile-response'), + 'remoteip' => $request->getClientIp(), + ]); + + if($r->successful()){ + + if($r->json()['success'] === true) { + // return response()->json(['message' => 'Captcha Success'], 200); + } else { + return response()->json(['message' => 'Captcha Failed'], 400); + } + } + + } + $account = (new CreateAccount($request->all(), $request->getClientIp()))->handle(); if (! ($account instanceof Account)) { return $account; diff --git a/app/Http/Controllers/TwilioController.php b/app/Http/Controllers/TwilioController.php index 61cff61ff31c..34763957425d 100644 --- a/app/Http/Controllers/TwilioController.php +++ b/app/Http/Controllers/TwilioController.php @@ -144,6 +144,9 @@ class TwilioController extends BaseController */ public function generate2faResetCode(Generate2faRequest $request) { + nlog($request->all()); + nlog($request->headers()); + $user = User::where('email', $request->email)->first(); if (!$user) { @@ -154,6 +157,11 @@ class TwilioController extends BaseController return response()->json(['message' => 'Please verify your email address before verifying your phone number'], 400); } + + if(!$user->first_name || !$user->last_name) { + return response()->json(['message' => 'Please update your first and/or last name in the User Details before verifying your number.'], 400); + } + if (!$user->phone || $user->phone == '') { return response()->json(['message' => 'User found, but no valid phone number on file, please contact support.'], 400); } diff --git a/config/ninja.php b/config/ninja.php index aade77cb0f13..b1fb25d661a0 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -228,5 +228,10 @@ return [ 'secret' => env('PAYPAL_SECRET', null), 'client_id' => env('PAYPAL_CLIENT_ID', null), 'webhook_id' => env('PAYPAL_WEBHOOK_ID', null), + ], + 'cloudflare' => [ + 'turnstile' => [ + 'secret' => env('CLOUDFLARE_SECRET', null), + ] ] ]; diff --git a/routes/api.php b/routes/api.php index a559d65e8e7c..9c00aaf92637 100644 --- a/routes/api.php +++ b/routes/api.php @@ -360,7 +360,7 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'] Route::post('settings/enable_two_factor', [TwoFactorController::class, 'enableTwoFactor']); Route::post('settings/disable_two_factor', [TwoFactorController::class, 'disableTwoFactor']); - Route::post('verify', [TwilioController::class, 'generate'])->name('verify.generate')->middleware('throttle:100,1'); + Route::post('verify', [TwilioController::class, 'generate'])->name('verify.generate')->middleware('throttle:3,1'); Route::post('verify/confirm', [TwilioController::class, 'confirm'])->name('verify.confirm'); Route::resource('vendors', VendorController::class); // name = (vendors. index / create / show / update / destroy / edit @@ -403,8 +403,8 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'] Route::post('api/v1/yodlee/status/{account_number}', [YodleeController::class, 'accountStatus']); }); -Route::post('api/v1/sms_reset', [TwilioController::class, 'generate2faResetCode'])->name('sms_reset.generate')->middleware('throttle:10,1'); -Route::post('api/v1/sms_reset/confirm', [TwilioController::class, 'confirm2faResetCode'])->name('sms_reset.confirm')->middleware('throttle:20,1'); +Route::post('api/v1/sms_reset', [TwilioController::class, 'generate2faResetCode'])->name('sms_reset.generate')->middleware('throttle:3,1'); +Route::post('api/v1/sms_reset/confirm', [TwilioController::class, 'confirm2faResetCode'])->name('sms_reset.confirm')->middleware('throttle:3,1'); Route::match(['get', 'post'], 'payment_webhook/{company_key}/{company_gateway_id}', PaymentWebhookController::class) ->middleware('throttle:1000,1') From f5248d8ac2fd26d57b3c614bd6684b0e61277e35 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 23 Dec 2023 16:09:03 +1100 Subject: [PATCH 5/6] Add RSA encryption/decryption helper --- app/Helpers/Encrypt/Secure.php | 41 ++++++++++++++++++++++++++++++++++ config/ninja.php | 6 ++++- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 app/Helpers/Encrypt/Secure.php diff --git a/app/Helpers/Encrypt/Secure.php b/app/Helpers/Encrypt/Secure.php new file mode 100644 index 000000000000..041fcc700230 --- /dev/null +++ b/app/Helpers/Encrypt/Secure.php @@ -0,0 +1,41 @@ + [ 'secret' => env('CLOUDFLARE_SECRET', null), ] - ] + ], + 'encryption' => [ + 'public_key' => env('NINJA_PUBLIC_KEY', false), + 'private_key' => env('NINJA_PRIVATE_KEY', false), + ], ]; From bc6faa282de50cbc6b178a9a385f3d727c1aa045 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 23 Dec 2023 16:14:26 +1100 Subject: [PATCH 6/6] Signup RSA hash checks --- app/Http/Controllers/AccountController.php | 25 +++++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index ce9e4918713f..0c1cea241993 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -11,17 +11,18 @@ namespace App\Http\Controllers; -use App\Http\Requests\Account\CreateAccountRequest; -use App\Http\Requests\Account\UpdateAccountRequest; -use App\Jobs\Account\CreateAccount; -use App\Libraries\MultiDB; use App\Models\Account; +use App\Libraries\MultiDB; +use App\Utils\TruthSource; use App\Models\CompanyUser; +use Illuminate\Http\Response; +use App\Helpers\Encrypt\Secure; +use App\Jobs\Account\CreateAccount; use App\Transformers\AccountTransformer; use App\Transformers\CompanyUserTransformer; -use App\Utils\TruthSource; use Illuminate\Foundation\Bus\DispatchesJobs; -use Illuminate\Http\Response; +use App\Http\Requests\Account\CreateAccountRequest; +use App\Http\Requests\Account\UpdateAccountRequest; class AccountController extends BaseController { @@ -66,7 +67,7 @@ class AccountController extends BaseController public function store(CreateAccountRequest $request) { - if(config('ninja.cloudflare.turnstile.secret')) { + if($request->has('cf-turnstile-response') && config('ninja.cloudflare.turnstile.secret')) { $r = \Illuminate\Support\Facades\Http::post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [ 'secret' => config('ninja.cloudflare.turnstile.secret'), 'response' => $request->input('cf-turnstile-response'), @@ -76,7 +77,7 @@ class AccountController extends BaseController if($r->successful()){ if($r->json()['success'] === true) { - // return response()->json(['message' => 'Captcha Success'], 200); + // Captcha passed } else { return response()->json(['message' => 'Captcha Failed'], 400); } @@ -84,6 +85,14 @@ class AccountController extends BaseController } + if($request->has('hash') && config('ninja.cloudflare.turnstile.secret')) { //@todo once all platforms are implemented, we disable access to the rest of this route without a success response. + + if(Secure::decrypt($request->input('hash')) !== $request->input('email')) { + return response()->json(['message' => 'Invalid Signup Payload'], 400); + } + + } + $account = (new CreateAccount($request->all(), $request->getClientIp()))->handle(); if (! ($account instanceof Account)) { return $account;