mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
commit
490639bd1c
41
app/Helpers/Encrypt/Secure.php
Normal file
41
app/Helpers/Encrypt/Secure.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Helpers\Encrypt;
|
||||
|
||||
class Secure
|
||||
{
|
||||
public static function encrypt(string $hash): ?string
|
||||
{
|
||||
$data = null;
|
||||
|
||||
$public_key = openssl_pkey_get_public(config('ninja.encryption.public_key'));
|
||||
|
||||
if (openssl_public_encrypt($hash, $encrypted, $public_key)) {
|
||||
$data = base64_encode($encrypted);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public static function decrypt(string $hash): ?string
|
||||
{
|
||||
$data = null;
|
||||
|
||||
$private_key = openssl_pkey_get_private(config('ninja.encryption.private_key'));
|
||||
|
||||
if (openssl_private_decrypt(base64_decode($hash), $decrypted, $private_key)) {
|
||||
$data = $decrypted;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
@ -65,6 +66,33 @@ class AccountController extends BaseController
|
||||
*/
|
||||
public function store(CreateAccountRequest $request)
|
||||
{
|
||||
|
||||
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'),
|
||||
'remoteip' => $request->getClientIp(),
|
||||
]);
|
||||
|
||||
if($r->successful()){
|
||||
|
||||
if($r->json()['success'] === true) {
|
||||
// Captcha passed
|
||||
} else {
|
||||
return response()->json(['message' => 'Captcha Failed'], 400);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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)) {
|
||||
@ -140,12 +144,24 @@ class TwilioController extends BaseController
|
||||
*/
|
||||
public function generate2faResetCode(Generate2faRequest $request)
|
||||
{
|
||||
nlog($request->all());
|
||||
nlog($request->headers());
|
||||
|
||||
$user = User::where('email', $request->email)->first();
|
||||
|
||||
if (!$user) {
|
||||
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->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);
|
||||
}
|
||||
|
@ -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 .= "<div class='flex px-5 block'><p class= w-36 block'>{$variable}_label</p><p class='pl-5 w-36 block entity-field'>{$variable}</p></div>";
|
||||
$entity_details .= "<div class='flex px-5 block'><p class= w-36 block'>{$variable}_label</p><p class='ml-5 w-36 block entity-field'>{$variable}</p></div>";
|
||||
}
|
||||
|
||||
} elseif($this->entity_type == 'quote') {
|
||||
foreach($this->settings->pdf_variables->quote_details ?? [] as $variable) {
|
||||
$entity_details .= "<div class='flex px-5 block'><p class= w-36 block'>{$variable}_label</p><p class='pl-5 w-36 block entity-field'>{$variable}</p></div>";
|
||||
$entity_details .= "<div class='flex px-5 block'><p class= w-36 block'>{$variable}_label</p><p class='ml-5 w-36 block entity-field'>{$variable}</p></div>";
|
||||
}
|
||||
} elseif($this->entity_type == 'credit') {
|
||||
foreach($this->settings->pdf_variables->credit_details ?? [] as $variable) {
|
||||
$entity_details .= "<div class='flex px-5 block'><p class= w-36 block'>{$variable}_label</p><p class='pl-5 w-36 block entity-field'>{$variable}</p></div>";
|
||||
$entity_details .= "<div class='flex px-5 block'><p class= w-36 block'>{$variable}_label</p><p class='ml-5 w-36 block entity-field'>{$variable}</p></div>";
|
||||
}
|
||||
} elseif($this->entity_type == 'purchase_order') {
|
||||
foreach($this->settings->pdf_variables->purchase_order_details ?? [] as $variable) {
|
||||
$entity_details .= "<div class='flex px-5 block'><p class= w-36 block'>{$variable}_label</p><p class='pl-5 w-36 block entity-field'>{$variable}</p></div>";
|
||||
$entity_details .= "<div class='flex px-5 block'><p class= w-36 block'>{$variable}_label</p><p class='ml-5 w-36 block entity-field'>{$variable}</p></div>";
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,6 +310,7 @@ class PdfSlot extends Component
|
||||
return 'purchase_order';
|
||||
}
|
||||
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -233,5 +233,14 @@ 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),
|
||||
]
|
||||
],
|
||||
'encryption' => [
|
||||
'public_key' => env('NINJA_PUBLIC_KEY', false),
|
||||
'private_key' => env('NINJA_PRIVATE_KEY', false),
|
||||
],
|
||||
];
|
||||
|
@ -77,6 +77,8 @@ span {
|
||||
<td>
|
||||
<div class="product-information">
|
||||
<div class="item-details">
|
||||
|
||||
<p class="overflow-ellipsis overflow-hidden px-1 mb-2">{!! $product['notes'] !!}</p>
|
||||
<p class="mt-2">
|
||||
@if($show_quantity)
|
||||
{{ $product['quantity'] }} x
|
||||
@ -85,8 +87,8 @@ span {
|
||||
@if($show_cost)
|
||||
{{ $product['cost'] }}
|
||||
@endif
|
||||
</p>
|
||||
<p class="overflow-ellipsis overflow-hidden px-1 mb-2">{!! $product['notes'] !!}</p>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
@ -361,7 +361,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
|
||||
@ -406,8 +406,8 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale']
|
||||
Route::get('nordigen/institutions', [NordigenController::class, 'institutions'])->name('nordigen.institutions');
|
||||
});
|
||||
|
||||
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')
|
||||
|
Loading…
x
Reference in New Issue
Block a user