Merge pull request #8538 from turbo124/v5-develop

Password timeout route
This commit is contained in:
David Bomba 2023-06-08 20:37:23 +10:00 committed by GitHub
commit 37ae5314f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 181 additions and 58 deletions

View File

@ -0,0 +1,28 @@
<?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\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
class PasswordTimeoutController extends Controller
{
public function __invoke()
{
$cached = Cache::get(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in');
return $cached ? response()->json(['message' => 'Password is valid'], 200) : response()->json(['message' => 'Invalid Password'], 412);
}
}

View File

@ -436,6 +436,14 @@ class MigrationController extends BaseController
StartMigration::dispatch($migration_file, $user, $fresh_company); StartMigration::dispatch($migration_file, $user, $fresh_company);
} }
} }
return response()->json([
'_id' => Str::uuid(),
'method' => config('queue.default'),
'started_at' => now(),
], 200);
} }
} }
} }

View File

@ -37,6 +37,7 @@ class PasswordProtection
'errors' => new stdClass, 'errors' => new stdClass,
]; ];
/** @var \App\Models\User auth()->user() */
$timeout = auth()->user()->company()->default_password_timeout; $timeout = auth()->user()->company()->default_password_timeout;
if ($timeout == 0) { if ($timeout == 0) {

View File

@ -72,6 +72,9 @@ class UpdateInvoiceRequest extends Request
$rules['tax_name2'] = 'bail|sometimes|string|nullable'; $rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable'; $rules['tax_name3'] = 'bail|sometimes|string|nullable';
// not needed.
// $rules['partial_due_date'] = 'bail|sometimes|required_unless:partial,0,null';
return $rules; return $rules;
} }

View File

@ -100,10 +100,7 @@ class Import implements ShouldQueue
use Uploadable; use Uploadable;
use SavesDocuments; use SavesDocuments;
/** private string $file_path; //the file path - using a different JSON parser here.
* @var array
*/
private $file_path; //the file path - using a different JSON parser here.
/** /**
* @var Company * @var Company
@ -202,7 +199,11 @@ class Import implements ShouldQueue
nlog($this->company->id); nlog($this->company->id);
auth()->login($this->user, false); auth()->login($this->user, false);
auth()->user()->setCompany($this->company);
/** @var \App\Models\User $user */
$user = auth()->user();
$user->setCompany($this->company);
$array = json_decode(file_get_contents($this->file_path), 1); $array = json_decode(file_get_contents($this->file_path), 1);
$data = $array['data']; $data = $array['data'];

View File

@ -11,26 +11,27 @@
namespace App\Jobs\Util; namespace App\Jobs\Util;
use App\Exceptions\ClientHostedMigrationException; use ZipArchive;
use App\Exceptions\MigrationValidatorFailed;
use App\Exceptions\NonExistingMigrationFile;
use App\Exceptions\ProcessingMigrationArchiveFailed;
use App\Exceptions\ResourceDependencyMissing;
use App\Exceptions\ResourceNotAvailableForMigration;
use App\Libraries\MultiDB;
use App\Mail\MigrationFailed;
use App\Models\Company;
use App\Models\User; use App\Models\User;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Models\Company;
use App\Libraries\MultiDB;
use App\Mail\MigrationFailed;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Cache;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use ZipArchive; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Exceptions\MigrationValidatorFailed;
use App\Exceptions\NonExistingMigrationFile;
use App\Exceptions\ResourceDependencyMissing;
use App\Exceptions\ClientHostedMigrationException;
use App\Exceptions\ProcessingMigrationArchiveFailed;
use App\Exceptions\ResourceNotAvailableForMigration;
class StartMigration implements ShouldQueue class StartMigration implements ShouldQueue
{ {
@ -79,6 +80,8 @@ class StartMigration implements ShouldQueue
{ {
nlog('Inside Migration Job'); nlog('Inside Migration Job');
Cache::put("migration-{$this->company->company_key}", "started", 86400);
set_time_limit(0); set_time_limit(0);
MultiDB::setDb($this->company->db); MultiDB::setDb($this->company->db);
@ -124,6 +127,8 @@ class StartMigration implements ShouldQueue
$this->company->update_products = $update_product_flag; $this->company->update_products = $update_product_flag;
$this->company->save(); $this->company->save();
Cache::put("migration-{$this->company->company_key}", "completed", 86400);
App::forgetInstance('translator'); App::forgetInstance('translator');
$t = app('translator'); $t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings)); $t->replace(Ninja::transformTranslations($this->company->settings));
@ -131,6 +136,8 @@ class StartMigration implements ShouldQueue
$this->company->update_products = $update_product_flag; $this->company->update_products = $update_product_flag;
$this->company->save(); $this->company->save();
Cache::put("migration-{$this->company->company_key}", "failed", 86400);
if (Ninja::isHosted()) { if (Ninja::isHosted()) {
app('sentry')->captureException($e); app('sentry')->captureException($e);
} }
@ -147,6 +154,9 @@ class StartMigration implements ShouldQueue
if (app()->environment() !== 'production') { if (app()->environment() !== 'production') {
info($e->getMessage()); info($e->getMessage());
} }
Storage::deleteDirectory(public_path("storage/migrations/{$filename}"));
} }
//always make sure we unset the migration as running //always make sure we unset the migration as running

View File

@ -53,6 +53,8 @@ class MigrationCompleted extends Mailable
$data['whitelabel'] = $this->company->account->isPaid() ? true : false; $data['whitelabel'] = $this->company->account->isPaid() ? true : false;
$data['check_data'] = $this->check_data ?: ''; $data['check_data'] = $this->check_data ?: '';
$data['logo'] = $this->company->present()->logo(); $data['logo'] = $this->company->present()->logo();
$data['url'] = Ninja::isHosted() ? config('ninja.react_url') : config('ninja.app_url');
$data = array_merge($data, [ $data = array_merge($data, [
'logo' => $this->company->present()->logo(), 'logo' => $this->company->present()->logo(),

View File

@ -40,7 +40,10 @@ use Illuminate\Support\Str;
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel scopeExclude() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel scopeExclude()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel find() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel find()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel whereIn() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel whereIn()
* @method static \Illuminate\Database\Eloquent\Builder|BankIntegration where() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel where()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel count()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel create()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel insert()
* @method \App\Models\Company company() * @method \App\Models\Company company()
* @method int companyId() * @method int companyId()
* @method Builder|static exclude($columns) * @method Builder|static exclude($columns)

View File

@ -168,6 +168,7 @@ use Laracasts\Presenter\PresentableTrait;
* @method static \Illuminate\Database\Eloquent\Builder|Client withTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Client withTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|Client withoutTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Client withoutTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|Client with() * @method static \Illuminate\Database\Eloquent\Builder|Client with()
* @method static \Illuminate\Database\Eloquent\Builder|Client where()
* @property string $payment_balance * @property string $payment_balance
* @method static \Illuminate\Database\Eloquent\Builder|Client wherePaymentBalance($value) * @method static \Illuminate\Database\Eloquent\Builder|Client wherePaymentBalance($value)
* @property mixed $tax_data * @property mixed $tax_data

View File

@ -37,6 +37,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @method static \Illuminate\Database\Eloquent\Builder|TaskStatus onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|TaskStatus onlyTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|TaskStatus query() * @method static \Illuminate\Database\Eloquent\Builder|TaskStatus query()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel scope() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel scope()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel insert()
* @method static \Illuminate\Database\Eloquent\Builder|TaskStatus whereColor($value) * @method static \Illuminate\Database\Eloquent\Builder|TaskStatus whereColor($value)
* @method static \Illuminate\Database\Eloquent\Builder|TaskStatus whereCompanyId($value) * @method static \Illuminate\Database\Eloquent\Builder|TaskStatus whereCompanyId($value)
* @method static \Illuminate\Database\Eloquent\Builder|TaskStatus whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|TaskStatus whereCreatedAt($value)

View File

@ -98,11 +98,10 @@ class PaymentMigrationRepository extends BaseRepository
} }
$payment->deleted_at = $data['deleted_at'] ?: null; $payment->deleted_at = $data['deleted_at'] ?: null;
$payment->save();
if ($payment->currency_id == 0) { if ($payment->currency_id == 0) {
$payment->currency_id = $payment->company->settings->currency_id; $payment->currency_id = $payment->company->settings->currency_id;
$payment->save();
} }
/*Ensure payment number generated*/ /*Ensure payment number generated*/
@ -110,6 +109,8 @@ class PaymentMigrationRepository extends BaseRepository
$payment->number = $payment->client->getNextPaymentNumber($payment->client, $payment); $payment->number = $payment->client->getNextPaymentNumber($payment->client, $payment);
} }
$payment->save();
$invoice_totals = 0; $invoice_totals = 0;
$credit_totals = 0; $credit_totals = 0;
$invoices = false; $invoices = false;

View File

@ -20,15 +20,10 @@ class ApplyNumber extends AbstractService
{ {
use GeneratesCounter; use GeneratesCounter;
private $payment;
private bool $completed = true; private bool $completed = true;
public function __construct(Payment $payment) public function __construct(private Payment $payment)
{ {
$this->client = $payment->client;
$this->payment = $payment;
} }
public function run() public function run()
@ -38,7 +33,6 @@ class ApplyNumber extends AbstractService
} }
$this->trySaving(); $this->trySaving();
// $this->payment->number = $this->getNextPaymentNumber($this->client, $this->payment);
return $this->payment; return $this->payment;
} }
@ -49,7 +43,7 @@ class ApplyNumber extends AbstractService
do { do {
try { try {
$this->payment->number = $this->getNextPaymentNumber($this->client, $this->payment); $this->payment->number = $this->getNextPaymentNumber($this->payment->client, $this->payment);
$this->payment->saveQuietly(); $this->payment->saveQuietly();
$this->completed = false; $this->completed = false;

View File

@ -708,7 +708,8 @@ trait GeneratesCounter
$replace[] = str_pad(($user_id), 2, '0', STR_PAD_LEFT); $replace[] = str_pad(($user_id), 2, '0', STR_PAD_LEFT);
} }
$matches = false; $matches = [];
preg_match('/{\$date:(.*?)}/', $pattern, $matches); preg_match('/{\$date:(.*?)}/', $pattern, $matches);
if (count($matches) > 1) { if (count($matches) > 1) {
$format = $matches[1]; $format = $matches[1];

View File

@ -29,7 +29,7 @@ trait Refundable
* Entry point for processing of refunds. * Entry point for processing of refunds.
* @param array $data * @param array $data
* @deprecated ???? 06-09-2022 * @deprecated ???? 06-09-2022
* @return Refundable * @return self
* @throws PaymentRefundFailed * @throws PaymentRefundFailed
*/ */
public function processRefund(array $data) public function processRefund(array $data)

View File

@ -108,7 +108,6 @@ class VendorHtmlEngine
{ {
if (! $this->vendor->currency()) { if (! $this->vendor->currency()) {
throw new Exception(debug_backtrace()[1]['function'], 1); throw new Exception(debug_backtrace()[1]['function'], 1);
exit;
} }
App::forgetInstance('translator'); App::forgetInstance('translator');
@ -601,6 +600,8 @@ class VendorHtmlEngine
* @return string a collection of <tr> rows with line item * @return string a collection of <tr> rows with line item
* aggregate data * aggregate data
*/ */
/*
private function makeLineTaxes() :string private function makeLineTaxes() :string
{ {
$tax_map = $this->entity_calc->getTaxMap(); $tax_map = $this->entity_calc->getTaxMap();
@ -616,18 +617,6 @@ class VendorHtmlEngine
return $data; return $data;
} }
private function lineTaxValues() :string
{
$tax_map = $this->entity_calc->getTaxMap();
$data = '';
foreach ($tax_map as $tax) {
$data .= '<span>'.Number::formatMoney($tax['total'], $this->company).'</span>';
}
return $data;
}
private function makeTotalTaxes() :string private function makeTotalTaxes() :string
{ {
@ -653,21 +642,13 @@ class VendorHtmlEngine
return strtr($section, $values); return strtr($section, $values);
} }
/*
| Ensures the URL doesn't have duplicated trailing slash
*/ */
public function generateAppUrl()
{
//return rtrim(config('ninja.app_url'), "/");
return config('ninja.app_url');
}
/** /**
* Builds CSS to assist with the generation * Builds CSS to assist with the generation
* of Repeating headers and footers on the PDF. * of Repeating headers and footers on the PDF.
* @return string The css string * @return string The css string
*/
private function generateCustomCSS() :string private function generateCustomCSS() :string
{ {
$header_and_footer = ' $header_and_footer = '
@ -759,6 +740,31 @@ html {
return $css; return $css;
} }
*/
private function lineTaxValues() :string
{
$tax_map = $this->entity_calc->getTaxMap();
$data = '';
foreach ($tax_map as $tax) {
$data .= '<span>'.Number::formatMoney($tax['total'], $this->company).'</span>';
}
return $data;
}
/*
| Ensures the URL doesn't have duplicated trailing slash
*/
public function generateAppUrl()
{
//return rtrim(config('ninja.app_url'), "/");
return config('ninja.app_url');
}
/** /**
* Generate markup for HTML images on entity. * Generate markup for HTML images on entity.

View File

@ -3976,7 +3976,7 @@ $LANG = array(
'add_payment_method_first' => 'add payment method', 'add_payment_method_first' => 'add payment method',
'no_items_selected' => 'No items selected.', 'no_items_selected' => 'No items selected.',
'payment_due' => 'Payment due', 'payment_due' => 'Payment due',
'account_balance' => 'Account balance', 'account_balance' => 'Account Balance',
'thanks' => 'Thanks', 'thanks' => 'Thanks',
'minimum_required_payment' => 'Minimum required payment is :amount', 'minimum_required_payment' => 'Minimum required payment is :amount',
'under_payments_disabled' => 'Company doesn\'t support under payments.', 'under_payments_disabled' => 'Company doesn\'t support under payments.',

View File

@ -2,9 +2,12 @@ includes:
- ./vendor/nunomaduro/larastan/extension.neon - ./vendor/nunomaduro/larastan/extension.neon
parameters: parameters:
treatPhpDocTypesAsCertain: false
ignoreErrors: ignoreErrors:
- '#Call to an undefined method .*badMethod\(\)#' - '#Call to an undefined method .*badMethod\(\)#'
- '#Call to an undefined method Illuminate\Database\Eloquent\Builder::exclude#' - '#Call to an undefined method Illuminate\Database\Eloquent\Builder::exclude#'
parallel:
maximumNumberOfProcesses: 8
level: 4 level: 4
paths: paths:
- 'app/' - 'app/'

View File

@ -106,7 +106,7 @@
<tbody> <tbody>
<tr> <tr>
<td align="center" class="new_button" style="border-radius: 2px; background-color: '.$this->settings->primary_color.'"> <td align="center" class="new_button" style="border-radius: 2px; background-color: '.$this->settings->primary_color.'">
<a href="{{ url('/') }}" target="_blank" class="new_button" style="text-decoration: none; border: 1px solid '.$this->settings->primary_color.'; display: inline-block; border-radius: 2px; padding-top: 15px; padding-bottom: 15px; padding-left: 25px; padding-right: 25px; font-size: 20px; color: #fff"> <a href="{{ $url }}" target="_blank" class="new_button" style="text-decoration: none; border: 1px solid '.$this->settings->primary_color.'; display: inline-block; border-radius: 2px; padding-top: 15px; padding-bottom: 15px; padding-left: 25px; padding-right: 25px; font-size: 20px; color: #fff">
<singleline label="cta button">{{ ctrans('texts.account_login') }}</singleline> <singleline label="cta button">{{ ctrans('texts.account_login') }}</singleline>
</a> </a>
</td> </td>

View File

@ -64,7 +64,7 @@ If your logo imported correctly it will available display below. If it didn't im
{!! ctrans('texts.documents') !!}: {!! count($company->documents) !!} {!! ctrans('texts.documents') !!}: {!! count($company->documents) !!}
@endif @endif
{!! url('/') !!} {!! $url !!}
{!! ctrans('texts.email_signature') !!} {!! ctrans('texts.email_signature') !!}

View File

@ -81,6 +81,7 @@ use App\Http\Controllers\Auth\ForgotPasswordController;
use App\Http\Controllers\BankTransactionRuleController; use App\Http\Controllers\BankTransactionRuleController;
use App\Http\Controllers\InAppPurchase\AppleController; use App\Http\Controllers\InAppPurchase\AppleController;
use App\Http\Controllers\Reports\QuoteReportController; use App\Http\Controllers\Reports\QuoteReportController;
use App\Http\Controllers\Auth\PasswordTimeoutController;
use App\Http\Controllers\PreviewPurchaseOrderController; use App\Http\Controllers\PreviewPurchaseOrderController;
use App\Http\Controllers\Reports\ClientReportController; use App\Http\Controllers\Reports\ClientReportController;
use App\Http\Controllers\Reports\CreditReportController; use App\Http\Controllers\Reports\CreditReportController;
@ -116,6 +117,8 @@ Route::group(['middleware' => ['throttle:login','api_secret_check','email_db']],
}); });
Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'], 'prefix' => 'api/v1', 'as' => 'api.'], function () { Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'], 'prefix' => 'api/v1', 'as' => 'api.'], function () {
Route::post('password_timeout', PasswordTimeoutController::class)->name('password_timeout');
Route::put('accounts/{account}', [AccountController::class, 'update'])->name('account.update'); Route::put('accounts/{account}', [AccountController::class, 'update'])->name('account.update');
Route::resource('bank_integrations', BankIntegrationController::class); // name = (clients. index / create / show / update / destroy / edit Route::resource('bank_integrations', BankIntegrationController::class); // name = (clients. index / create / show / update / destroy / edit
Route::post('bank_integrations/refresh_accounts', [BankIntegrationController::class, 'refreshAccounts'])->name('bank_integrations.refresh_accounts')->middleware('throttle:30,1'); Route::post('bank_integrations/refresh_accounts', [BankIntegrationController::class, 'refreshAccounts'])->name('bank_integrations.refresh_accounts')->middleware('throttle:30,1');

View File

@ -0,0 +1,57 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace Tests\Unit;
use Tests\TestCase;
use Tests\MockAccountData;
use Illuminate\Support\Facades\Cache;
use Illuminate\Foundation\Testing\DatabaseTransactions;
/**
* @test
* @covers App\Http\Controllers\Auth\PasswordTimeoutController
*/
class PasswordTimeoutTest extends TestCase
{
use DatabaseTransactions;
use MockAccountData;
protected function setUp() :void
{
parent::setUp();
$this->makeTestData();
}
public function testFalseResponse()
{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/password_timeout')
->assertStatus(412);
}
public function testTrueResponse()
{
Cache::put($this->user->hashed_id.'_'.$this->user->account_id.'_logged_in', true, 3600);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/password_timeout')
->assertStatus(200);
}
}