diff --git a/app/Api/V1/Requests/AttachmentStoreRequest.php b/app/Api/V1/Requests/AttachmentStoreRequest.php index 6fc7a89354..ab74872dd2 100644 --- a/app/Api/V1/Requests/AttachmentStoreRequest.php +++ b/app/Api/V1/Requests/AttachmentStoreRequest.php @@ -23,9 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests; -use FireflyIII\Models\Bill; -use FireflyIII\Models\ImportJob; -use FireflyIII\Models\TransactionJournal; use FireflyIII\Rules\IsValidAttachmentModel; /** @@ -69,14 +66,14 @@ class AttachmentStoreRequest extends Request */ public function rules(): array { - $models = implode( - ',', - [ - str_replace('FireflyIII\\Models\\', '', Bill::class), - str_replace('FireflyIII\\Models\\', '', ImportJob::class), - str_replace('FireflyIII\\Models\\', '', TransactionJournal::class), - ] + $models = config('firefly.valid_attachment_models'); + $models = array_map( + + static function (string $className) { + return str_replace('FireflyIII\\Models\\', '', $className); + }, $models ); + $models = implode(',', $models); $model = $this->string('attachable_type'); return [ diff --git a/app/Rules/IsValidAttachmentModel.php b/app/Rules/IsValidAttachmentModel.php index d6f2a2c404..441f78b0f9 100644 --- a/app/Rules/IsValidAttachmentModel.php +++ b/app/Rules/IsValidAttachmentModel.php @@ -24,15 +24,24 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use FireflyIII\Models\Account; use FireflyIII\Models\Bill; +use FireflyIII\Models\Budget; +use FireflyIII\Models\Category; use FireflyIII\Models\ImportJob; +use FireflyIII\Models\PiggyBank; +use FireflyIII\Models\Tag; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Repositories\Journal\JournalAPIRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; -use FireflyIII\User; +use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Contracts\Validation\Rule; use Log; @@ -80,53 +89,150 @@ class IsValidAttachmentModel implements Rule if (!auth()->check()) { return false; } + $methods = [ + Account::class => 'validateAccount', + Bill::class => 'validateBill', + Budget::class => 'validateBudget', + Category::class => 'validateCategory', + ImportJob::class => 'validateImportJob', + PiggyBank::class => 'validatePiggyBank', + Tag::class => 'validateTag', + Transaction::class => 'validateTransaction', + TransactionJournal::class => 'validateJournal', + ]; + if (!isset($methods[$this->model])) { + Log::error(sprintf('Cannot validate model "%s" in %s.', $this->model, __METHOD__)); - - if (Bill::class === $this->model) { - /** @var BillRepositoryInterface $repository */ - $repository = app(BillRepositoryInterface::class); - /** @var User $user */ - $user = auth()->user(); - $repository->setUser($user); - $bill = $repository->find((int)$value); - - return null !== $bill; + return false; } + $method = $methods[$this->model]; - if (ImportJob::class === $this->model) { - /** @var ImportJobRepositoryInterface $repository */ - $repository = app(ImportJobRepositoryInterface::class); - /** @var User $user */ - $user = auth()->user(); - $repository->setUser($user); - $importJob = $repository->find((int)$value); + return $this->$method((int) $value); + } - return null !== $importJob; - } + /** + * @param int $value + * + * @return bool + */ + private function validateTag(int $value): bool + { + /** @var TagRepositoryInterface $repository */ + $repository = app(TagRepositoryInterface::class); + $repository->setUser(auth()->user()); - if (Transaction::class === $this->model) { - /** @var JournalAPIRepositoryInterface $repository */ - $repository = app(JournalAPIRepositoryInterface::class); + return null !== $repository->findNull($value); + } - /** @var User $user */ - $user = auth()->user(); - $repository->setUser($user); - $transaction = $repository->findTransaction((int)$value); + /** + * @param int $value + * + * @return bool + */ + private function validatePiggyBank(int $value): bool + { + /** @var PiggyBankRepositoryInterface $repository */ + $repository = app(PiggyBankRepositoryInterface::class); + $repository->setUser(auth()->user()); - return null !== $transaction; - } + return null !== $repository->findNull($value); + } - if (TransactionJournal::class === $this->model) { - $repository = app(JournalRepositoryInterface::class); - $user = auth()->user(); - $repository->setUser($user); - $result = $repository->findNull((int)$value); + /** + * @param int $value + * + * @return bool + */ + private function validateBudget(int $value): bool + { + /** @var BudgetRepositoryInterface $repository */ + $repository = app(BudgetRepositoryInterface::class); + $repository->setUser(auth()->user()); - return null !== $result; - } - Log::error(sprintf('No model was recognized from string "%s"', $this->model)); + return null !== $repository->findNull($value); + } - return false; + /** + * @param int $value + * + * @return bool + */ + private function validateCategory(int $value): bool + { + /** @var CategoryRepositoryInterface $repository */ + $repository = app(CategoryRepositoryInterface::class); + $repository->setUser(auth()->user()); + + return null !== $repository->findNull($value); + } + + /** + * @param int $value + * + * @return bool + */ + private function validateAccount(int $value): bool + { + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + $repository->setUser(auth()->user()); + + return null !== $repository->findNull($value); + } + + /** + * @param int $value + * + * @return bool + */ + private function validateJournal(int $value): bool + { + $repository = app(JournalRepositoryInterface::class); + $repository->setUser(auth()->user()); + + return null !== $repository->findNull($value); + } + + /** + * @param int $value + * + * @return bool + */ + private function validateTransaction(int $value): bool + { + /** @var JournalAPIRepositoryInterface $repository */ + $repository = app(JournalAPIRepositoryInterface::class); + $repository->setUser(auth()->user()); + + return null !== $repository->findTransaction((int) $value); + } + + /** + * @param int $value + * + * @return bool + */ + private function validateImportJob(int $value): bool + { + /** @var ImportJobRepositoryInterface $repository */ + $repository = app(ImportJobRepositoryInterface::class); + $repository->setUser(auth()->user()); + + return null !== $repository->find($value); + } + + /** + * @param int $value + * + * @return bool + */ + private function validateBill(int $value): bool + { + /** @var BillRepositoryInterface $repository */ + $repository = app(BillRepositoryInterface::class); + $repository->setUser(auth()->user()); + + return null !== $repository->find($value); } /** @@ -141,6 +247,7 @@ class IsValidAttachmentModel implements Rule $model = str_replace($search, $replace, $model); $model = sprintf('FireflyIII\Models\%s', $model); + return $model; } } diff --git a/config/firefly.php b/config/firefly.php index 7c23ddde8e..d61dd1a4e2 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -38,6 +38,7 @@ use FireflyIII\Models\Recurrence; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; use FireflyIII\Models\Tag; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; @@ -136,38 +137,49 @@ return [ 'telemetry' => false, ], - 'encryption' => null === env('USE_ENCRYPTION') || true === env('USE_ENCRYPTION'), - 'version' => '5.1.1', - 'api_version' => '1.0.2', - 'db_version' => 12, - 'maxUploadSize' => 15242880, - 'send_error_message' => env('SEND_ERROR_MESSAGE', true), - 'site_owner' => env('SITE_OWNER', ''), - 'send_registration_mail' => env('SEND_REGISTRATION_MAIL', true), - 'demo_username' => env('DEMO_USERNAME', ''), - 'demo_password' => env('DEMO_PASSWORD', ''), - 'is_sandstorm' => env('IS_SANDSTORM', 'unknown'), - 'bunq_use_sandbox' => env('BUNQ_USE_SANDBOX', false), - 'fixer_api_key' => env('FIXER_API_KEY', ''), - 'mapbox_api_key' => env('MAPBOX_API_KEY', ''), - 'trusted_proxies' => env('TRUSTED_PROXIES', ''), - 'search_result_limit' => env('SEARCH_RESULT_LIMIT', 50), - 'send_report_journals' => envNonEmpty('SEND_REPORT_JOURNALS', true), - 'tracker_site_id' => env('TRACKER_SITE_ID', ''), - 'tracker_url' => env('TRACKER_URL', ''), - 'disable_frame_header' => env('DISABLE_FRAME_HEADER', false), - 'disable_csp_header' => env('DISABLE_CSP_HEADER', false), - 'login_provider' => envNonEmpty('LOGIN_PROVIDER', 'eloquent'), - 'cer_provider' => envNonEmpty('CER_PROVIDER', 'fixer'), - 'update_endpoint' => 'https://version.firefly-iii.org/index.json', - 'send_telemetry' => env('SEND_TELEMETRY', false), - 'update_minimum_age' => 6, - 'default_location' => [ + 'encryption' => null === env('USE_ENCRYPTION') || true === env('USE_ENCRYPTION'), + 'version' => '5.1.1', + 'api_version' => '1.0.2', + 'db_version' => 12, + 'maxUploadSize' => 15242880, + 'send_error_message' => env('SEND_ERROR_MESSAGE', true), + 'site_owner' => env('SITE_OWNER', ''), + 'send_registration_mail' => env('SEND_REGISTRATION_MAIL', true), + 'demo_username' => env('DEMO_USERNAME', ''), + 'demo_password' => env('DEMO_PASSWORD', ''), + 'is_sandstorm' => env('IS_SANDSTORM', 'unknown'), + 'bunq_use_sandbox' => env('BUNQ_USE_SANDBOX', false), + 'fixer_api_key' => env('FIXER_API_KEY', ''), + 'mapbox_api_key' => env('MAPBOX_API_KEY', ''), + 'trusted_proxies' => env('TRUSTED_PROXIES', ''), + 'search_result_limit' => env('SEARCH_RESULT_LIMIT', 50), + 'send_report_journals' => envNonEmpty('SEND_REPORT_JOURNALS', true), + 'tracker_site_id' => env('TRACKER_SITE_ID', ''), + 'tracker_url' => env('TRACKER_URL', ''), + 'disable_frame_header' => env('DISABLE_FRAME_HEADER', false), + 'disable_csp_header' => env('DISABLE_CSP_HEADER', false), + 'login_provider' => envNonEmpty('LOGIN_PROVIDER', 'eloquent'), + 'cer_provider' => envNonEmpty('CER_PROVIDER', 'fixer'), + 'update_endpoint' => 'https://version.firefly-iii.org/index.json', + 'send_telemetry' => env('SEND_TELEMETRY', false), + 'update_minimum_age' => 6, + 'default_location' => [ 'longitude' => env('MAP_DEFAULT_LONG', '5.916667'), 'latitude' => env('MAP_DEFAULT_LAT', '51.983333'), 'zoom_level' => env('MAP_DEFAULT_ZOOM', '6'), ], - 'allowedMimes' => [ + 'valid_attachment_models' => [ + Account::class, + Bill::class, + Budget::class, + Category::class, + ImportJob::class, + PiggyBank::class, + Tag::class, + Transaction::class, + TransactionJournal::class, + ], + 'allowedMimes' => [ /* plain files */ 'text/plain', @@ -545,8 +557,8 @@ return [ 'default_currency' => 'EUR', 'default_language' => envNonEmpty('DEFAULT_LANGUAGE', 'en_US'), 'search_modifiers' => ['amount_is', 'amount', 'amount_max', 'amount_min', 'amount_less', 'amount_more', 'source', 'destination', 'category', - 'budget', 'bill', 'type', 'date', 'date_before', 'date_after', 'on', 'before', 'after', 'from', 'to', 'tag', 'created_on', - 'updated_on', ], + 'budget', 'bill', 'type', 'date', 'date_before', 'date_after', 'on', 'before', 'after', 'from', 'to', 'tag', 'created_on', + 'updated_on',], // TODO notes has_attachments 'cer_providers' => [ @@ -559,10 +571,10 @@ return [ 'source' => [ TransactionTypeModel::WITHDRAWAL => [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE], TransactionTypeModel::DEPOSIT => [AccountType::REVENUE, AccountType::CASH, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE, - AccountType::INITIAL_BALANCE, AccountType::RECONCILIATION, ], + AccountType::INITIAL_BALANCE, AccountType::RECONCILIATION,], TransactionTypeModel::TRANSFER => [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE], TransactionTypeModel::OPENING_BALANCE => [AccountType::INITIAL_BALANCE, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, - AccountType::MORTGAGE, ], + AccountType::MORTGAGE,], TransactionTypeModel::RECONCILIATION => [AccountType::RECONCILIATION, AccountType::ASSET], // in case no transaction type is known yet, it could be anything. 'none' => [ @@ -576,43 +588,43 @@ return [ ], 'destination' => [ TransactionTypeModel::WITHDRAWAL => [AccountType::EXPENSE, AccountType::CASH, AccountType::LOAN, AccountType::DEBT, - AccountType::MORTGAGE, ], + AccountType::MORTGAGE,], TransactionTypeModel::DEPOSIT => [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE], TransactionTypeModel::TRANSFER => [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE], TransactionTypeModel::OPENING_BALANCE => [AccountType::INITIAL_BALANCE, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, - AccountType::MORTGAGE, ], + AccountType::MORTGAGE,], TransactionTypeModel::RECONCILIATION => [AccountType::RECONCILIATION, AccountType::ASSET], ], ], 'allowed_opposing_types' => [ 'source' => [ AccountType::ASSET => [AccountType::ASSET, AccountType::CASH, AccountType::DEBT, AccountType::EXPENSE, AccountType::INITIAL_BALANCE, - AccountType::LOAN, AccountType::RECONCILIATION, ], + AccountType::LOAN, AccountType::RECONCILIATION,], AccountType::CASH => [AccountType::ASSET], AccountType::DEBT => [AccountType::ASSET, AccountType::DEBT, AccountType::EXPENSE, AccountType::INITIAL_BALANCE, AccountType::LOAN, - AccountType::MORTGAGE, ], + AccountType::MORTGAGE,], AccountType::EXPENSE => [], // is not allowed as a source. AccountType::INITIAL_BALANCE => [AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE], AccountType::LOAN => [AccountType::ASSET, AccountType::DEBT, AccountType::EXPENSE, AccountType::INITIAL_BALANCE, AccountType::LOAN, - AccountType::MORTGAGE, ], + AccountType::MORTGAGE,], AccountType::MORTGAGE => [AccountType::ASSET, AccountType::DEBT, AccountType::EXPENSE, AccountType::INITIAL_BALANCE, AccountType::LOAN, - AccountType::MORTGAGE, ], + AccountType::MORTGAGE,], AccountType::RECONCILIATION => [AccountType::ASSET], AccountType::REVENUE => [AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE], ], 'destination' => [ AccountType::ASSET => [AccountType::ASSET, AccountType::CASH, AccountType::DEBT, AccountType::INITIAL_BALANCE, AccountType::LOAN, - AccountType::MORTGAGE, AccountType::RECONCILIATION, AccountType::REVENUE, ], + AccountType::MORTGAGE, AccountType::RECONCILIATION, AccountType::REVENUE,], AccountType::CASH => [AccountType::ASSET], AccountType::DEBT => [AccountType::ASSET, AccountType::DEBT, AccountType::INITIAL_BALANCE, AccountType::LOAN, AccountType::MORTGAGE, - AccountType::REVENUE, ], + AccountType::REVENUE,], AccountType::EXPENSE => [AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE], AccountType::INITIAL_BALANCE => [AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE], AccountType::LOAN => [AccountType::ASSET, AccountType::DEBT, AccountType::INITIAL_BALANCE, AccountType::LOAN, AccountType::MORTGAGE, - AccountType::REVENUE, ], + AccountType::REVENUE,], AccountType::MORTGAGE => [AccountType::ASSET, AccountType::DEBT, AccountType::INITIAL_BALANCE, AccountType::LOAN, AccountType::MORTGAGE, - AccountType::REVENUE, ], + AccountType::REVENUE,], AccountType::RECONCILIATION => [AccountType::ASSET], AccountType::REVENUE => [], // is not allowed as a destination ], @@ -621,29 +633,29 @@ return [ 'allowed_transaction_types' => [ 'source' => [ AccountType::ASSET => [TransactionTypeModel::WITHDRAWAL, TransactionTypeModel::TRANSFER, TransactionTypeModel::OPENING_BALANCE, - TransactionTypeModel::RECONCILIATION, ], + TransactionTypeModel::RECONCILIATION,], AccountType::EXPENSE => [], // is not allowed as a source. AccountType::REVENUE => [TransactionTypeModel::DEPOSIT], AccountType::LOAN => [TransactionTypeModel::WITHDRAWAL, TransactionTypeModel::DEPOSIT, TransactionTypeModel::TRANSFER, - TransactionTypeModel::OPENING_BALANCE, ], + TransactionTypeModel::OPENING_BALANCE,], AccountType::DEBT => [TransactionTypeModel::WITHDRAWAL, TransactionTypeModel::DEPOSIT, TransactionTypeModel::TRANSFER, - TransactionTypeModel::OPENING_BALANCE, ], + TransactionTypeModel::OPENING_BALANCE,], AccountType::MORTGAGE => [TransactionTypeModel::WITHDRAWAL, TransactionTypeModel::DEPOSIT, TransactionTypeModel::TRANSFER, - TransactionTypeModel::OPENING_BALANCE, ], + TransactionTypeModel::OPENING_BALANCE,], AccountType::INITIAL_BALANCE => [], // todo fill me in. AccountType::RECONCILIATION => [], // todo fill me in. ], 'destination' => [ AccountType::ASSET => [TransactionTypeModel::DEPOSIT, TransactionTypeModel::TRANSFER, TransactionTypeModel::OPENING_BALANCE, - TransactionTypeModel::RECONCILIATION, ], + TransactionTypeModel::RECONCILIATION,], AccountType::EXPENSE => [TransactionTypeModel::WITHDRAWAL], AccountType::REVENUE => [], // is not allowed as destination. AccountType::LOAN => [TransactionTypeModel::WITHDRAWAL, TransactionTypeModel::DEPOSIT, TransactionTypeModel::TRANSFER, - TransactionTypeModel::OPENING_BALANCE, ], + TransactionTypeModel::OPENING_BALANCE,], AccountType::DEBT => [TransactionTypeModel::WITHDRAWAL, TransactionTypeModel::DEPOSIT, TransactionTypeModel::TRANSFER, - TransactionTypeModel::OPENING_BALANCE, ], + TransactionTypeModel::OPENING_BALANCE,], AccountType::MORTGAGE => [TransactionTypeModel::WITHDRAWAL, TransactionTypeModel::DEPOSIT, TransactionTypeModel::TRANSFER, - TransactionTypeModel::OPENING_BALANCE, ], + TransactionTypeModel::OPENING_BALANCE,], AccountType::INITIAL_BALANCE => [], // todo fill me in. AccountType::RECONCILIATION => [], // todo fill me in. ],