Fix several issues with bunq import #1330

This commit is contained in:
James Cole 2018-04-10 21:18:38 +02:00
parent 0847040017
commit 90cf7a3bf5
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
22 changed files with 243 additions and 69 deletions

View File

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace FireflyIII\Import\Prerequisites;
use FireflyIII\Services\IP\IPRetrievalInterface;
use FireflyIII\User;
use Illuminate\Http\Request;
use Illuminate\Support\MessageBag;
@ -56,13 +57,23 @@ class BunqPrerequisites implements PrerequisitesInterface
public function getViewParameters(): array
{
Log::debug('Now in BunqPrerequisites::getViewParameters()');
$apiKey = Preferences::getForUser($this->user, 'bunq_api_key', null);
$string = '';
if (null !== $apiKey) {
$string = $apiKey->data;
$key = '';
$serverIP = '';
if ($this->hasApiKey()) {
$key = Preferences::getForUser($this->user, 'bunq_api_key', null)->data;
}
if ($this->hasServerIP()) {
$serverIP = Preferences::getForUser($this->user, 'external_ip', null)->data;
}
if (!$this->hasServerIP()) {
/** @var IPRetrievalInterface $service */
$service = app(IPRetrievalInterface::class);
$serverIP = (string)$service->getIP();
}
return ['key' => $string];
// get IP address
return ['key' => $key, 'ip' => $serverIP];
}
/**
@ -74,15 +85,10 @@ class BunqPrerequisites implements PrerequisitesInterface
*/
public function hasPrerequisites(): bool
{
Log::debug('Now in BunqPrerequisites::hasPrerequisites()');
$apiKey = Preferences::getForUser($this->user, 'bunq_api_key', false);
$result = (false === $apiKey->data || null === $apiKey->data);
$hasApiKey = $this->hasApiKey();
$hasServerIP = $this->hasServerIP();
Log::debug(sprintf('Is apiKey->data false? %s', var_export(false === $apiKey->data, true)));
Log::debug(sprintf('Is apiKey->data NULL? %s', var_export(null === $apiKey->data, true)));
Log::debug(sprintf('Result is: %s', var_export($result, true)));
return $result;
return !$hasApiKey || !$hasServerIP;
}
/**
@ -94,8 +100,6 @@ class BunqPrerequisites implements PrerequisitesInterface
{
Log::debug(sprintf('Now in setUser(#%d)', $user->id));
$this->user = $user;
return;
}
/**
@ -108,10 +112,50 @@ class BunqPrerequisites implements PrerequisitesInterface
*/
public function storePrerequisites(Request $request): MessageBag
{
$apiKey = $request->get('api_key');
$apiKey = $request->get('api_key');
$serverIP = $request->get('external_ip');
Log::debug('Storing bunq API key');
Preferences::setForUser($this->user, 'bunq_api_key', $apiKey);
Preferences::setForUser($this->user, 'external_ip', $serverIP);
return new MessageBag;
}
/**
* @return bool
*/
private function hasApiKey(): bool
{
$apiKey = Preferences::getForUser($this->user, 'bunq_api_key', false);
if (null === $apiKey) {
return false;
}
if (null === $apiKey->data) {
return false;
}
if (\strlen((string)$apiKey->data) === 64) {
return true;
}
return false;
}
/**
* @return bool
*/
private function hasServerIP(): bool
{
$serverIP = Preferences::getForUser($this->user, 'external_ip', false);
if (null === $serverIP) {
return false;
}
if (null === $serverIP->data) {
return false;
}
if (\strlen((string)$serverIP->data) > 6) {
return true;
}
return false;
}
}

View File

@ -25,7 +25,6 @@ namespace FireflyIII\Import\Routine;
use Carbon\Carbon;
use DB;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountFactory;
use FireflyIII\Factory\TransactionJournalFactory;
@ -53,10 +52,10 @@ use FireflyIII\Services\Bunq\Request\ListMonetaryAccountRequest;
use FireflyIII\Services\Bunq\Request\ListPaymentRequest;
use FireflyIII\Services\Bunq\Token\InstallationToken;
use FireflyIII\Services\Bunq\Token\SessionToken;
use FireflyIII\Services\IP\IPRetrievalInterface;
use Illuminate\Support\Collection;
use Log;
use Preferences;
use Requests;
/**
* Class BunqRoutine
@ -212,7 +211,7 @@ class BunqRoutine implements RoutineInterface
/**
* @throws FireflyException
*/
protected function runStageInitial()
protected function runStageInitial(): void
{
$this->addStep();
Log::debug('In runStageInitial()');
@ -237,8 +236,8 @@ class BunqRoutine implements RoutineInterface
{
$this->addStep();
Log::debug('Now in runStageRegistered()');
$apiKey = Preferences::getForUser($this->job->user, 'bunq_api_key')->data;
$serverPublicKey = Preferences::getForUser($this->job->user, 'bunq_server_public_key')->data;
$apiKey = (string)Preferences::getForUser($this->job->user, 'bunq_api_key')->data;
$serverPublicKey = new ServerPublicKey(Preferences::getForUser($this->job->user, 'bunq_server_public_key', [])->data);
$installationToken = $this->getInstallationToken();
$request = new DeviceSessionRequest;
$request->setInstallationToken($installationToken);
@ -265,14 +264,12 @@ class BunqRoutine implements RoutineInterface
$this->addStep();
Log::debug('Session stored in job.');
return;
}
/**
* Shorthand method.
*/
private function addStep()
private function addStep(): void
{
$this->addSteps(1);
}
@ -282,17 +279,17 @@ class BunqRoutine implements RoutineInterface
*
* @param int $count
*/
private function addSteps(int $count)
private function addSteps(int $count): void
{
$this->repository->addStepsDone($this->job, $count);
}
/**
* Shorthand
* Shorthand method
*
* @param int $steps
*/
private function addTotalSteps(int $steps)
private function addTotalSteps(int $steps): void
{
$this->repository->addTotalSteps($this->job, $steps);
}
@ -334,7 +331,7 @@ class BunqRoutine implements RoutineInterface
// try to find asset account just in case:
if ($expectedType !== AccountType::ASSET) {
$result = $this->accountRepository->findByIbanNull($party->getIban(), [AccountType::ASSET]);
if (nul !== $result) {
if (null !== $result) {
Log::debug(sprintf('Search for Asset "%s" resulted in account %s (#%d)', $party->getIban(), $result->name, $result->id));
return $result;
@ -403,6 +400,8 @@ class BunqRoutine implements RoutineInterface
}
/**
* Shorthand method.
*
* @return array
*/
private function getConfig(): array
@ -466,7 +465,7 @@ class BunqRoutine implements RoutineInterface
if (null !== $token) {
Log::debug('Have installation token, return it.');
return $token->data;
return new InstallationToken($token->data);
}
Log::debug('Have no installation token, request one.');
@ -481,9 +480,12 @@ class BunqRoutine implements RoutineInterface
$installationId = $request->getInstallationId();
$serverPublicKey = $request->getServerPublicKey();
Preferences::setForUser($this->job->user, 'bunq_installation_token', $installationToken);
Preferences::setForUser($this->job->user, 'bunq_installation_id', $installationId);
Preferences::setForUser($this->job->user, 'bunq_server_public_key', $serverPublicKey);
Log::debug('Have all values from InstallationTokenRequest');
Preferences::setForUser($this->job->user, 'bunq_installation_token', $installationToken->toArray());
Preferences::setForUser($this->job->user, 'bunq_installation_id', $installationId->toArray());
Preferences::setForUser($this->job->user, 'bunq_server_public_key', $serverPublicKey->toArray());
Log::debug('Stored token, ID and pub key.');
@ -507,7 +509,7 @@ class BunqRoutine implements RoutineInterface
$preference = Preferences::getForUser($this->job->user, 'bunq_private_key', null);
Log::debug('Return private key for user');
return $preference->data;
return (string)$preference->data;
}
/**
@ -527,7 +529,7 @@ class BunqRoutine implements RoutineInterface
$preference = Preferences::getForUser($this->job->user, 'bunq_public_key', null);
Log::debug('Return public key for user');
return $preference->data;
return (string)$preference->data;
}
/**
@ -537,20 +539,18 @@ class BunqRoutine implements RoutineInterface
*
* @throws FireflyException
*/
private function getRemoteIp(): string
private function getRemoteIp(): ?string
{
$preference = Preferences::getForUser($this->job->user, 'external_ip', null);
if (null === $preference) {
try {
$response = Requests::get('https://api.ipify.org');
} catch (Exception $e) {
throw new FireflyException(sprintf('Could not retrieve external IP: %s', $e->getMessage()));
/** @var IPRetrievalInterface $service */
$service = app(IPRetrievalInterface::class);
$serverIp = $service->getIP();
if (null !== $serverIp) {
Preferences::setForUser($this->job->user, 'external_ip', $serverIp);
}
if (200 !== $response->status_code) {
throw new FireflyException(sprintf('Could not retrieve external IP: %d %s', $response->status_code, $response->body));
}
$serverIp = $response->body;
Preferences::setForUser($this->job->user, 'external_ip', $serverIp);
return $serverIp;
}
@ -572,7 +572,7 @@ class BunqRoutine implements RoutineInterface
throw new FireflyException('Cannot determine bunq server public key, but should have it at this point.');
}
return $pref;
return new ServerPublicKey($pref);
}
/**
@ -738,7 +738,7 @@ class BunqRoutine implements RoutineInterface
if (null !== $deviceServerId) {
Log::debug('Already have device server ID.');
return $deviceServerId->data;
return new DeviceServerId($deviceServerId->data);
}
Log::debug('Device server ID is null, we have to find an existing one or register a new one.');
@ -778,8 +778,8 @@ class BunqRoutine implements RoutineInterface
throw new FireflyException('Was not able to register server with bunq. Please see the log files.');
}
Preferences::setForUser($this->job->user, 'bunq_device_server_id', $deviceServerId);
Log::debug(sprintf('Server ID: %s', serialize($deviceServerId)));
Preferences::setForUser($this->job->user, 'bunq_device_server_id', $deviceServerId->toArray());
Log::debug(sprintf('Server ID: %s', json_encode($deviceServerId)));
return $deviceServerId;
}

View File

@ -73,7 +73,7 @@ class Preference extends Model
unserialize($data, ['allowed_classes' => false]);
} catch (Exception $e) {
$serialized = false;
Log::debug(sprintf('Could not unserialise preference #%d. This is good. %s', $this->id, $e->getMessage()));
Log::debug(sprintf('Could not unserialise preference #%d ("%s"). This is good. %s', $this->id, $this->name, $e->getMessage()));
}
if (!$serialized) {
$result = json_decode($data, true);

View File

@ -48,6 +48,8 @@ use FireflyIII\Repositories\User\UserRepository;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Services\Currency\ExchangeRateInterface;
use FireflyIII\Services\Currency\FixerIOv2;
use FireflyIII\Services\IP\IpifyOrg;
use FireflyIII\Services\IP\IPRetrievalInterface;
use FireflyIII\Services\Password\PwndVerifierV2;
use FireflyIII\Services\Password\Verifier;
use FireflyIII\Support\Amount;
@ -180,5 +182,8 @@ class FireflyServiceProvider extends ServiceProvider
// password verifier thing
$this->app->bind(Verifier::class, PwndVerifierV2::class);
// IP thing:
$this->app->bind(IPRetrievalInterface::class, IpifyOrg::class);
}
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Services\Bunq\Id;
use Log;
/**
* Class BunqId.
*/

View File

@ -51,6 +51,10 @@ class Avatar extends BunqObject
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
return [
'uuid' => $this->uuid,
'anchor_uuid' => $this->anchorUuid,
'image' => $this->image->toArray(),
];
}
}

View File

@ -25,6 +25,8 @@ namespace FireflyIII\Services\Bunq\Object;
use Carbon\Carbon;
use FireflyIII\Services\Bunq\Id\DeviceServerId;
use FireflyIII\Exceptions\FireflyException;
/**
* Class DeviceServer
*/
@ -82,6 +84,6 @@ class DeviceServer extends BunqObject
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
throw new FireflyException(sprintf('Cannot convert %s to array.', \get_class($this)));
}
}

View File

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace FireflyIII\Services\Bunq\Object;
/**
* Class Image
*/
@ -56,7 +55,12 @@ class Image extends BunqObject
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
return [
'attachment_public_uuid' => $this->attachmentPublicUuid,
'height' => $this->height,
'width' => $this->width,
'content_type' => $this->contentType,
];
}
}

View File

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Services\Bunq\Object;
use FireflyIII\Exceptions\FireflyException;
/**
* Class LabelMonetaryAccount
*/
@ -75,7 +75,7 @@ class LabelMonetaryAccount extends BunqObject
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
throw new FireflyException(sprintf('Cannot convert %s to array.', \get_class($this)));
}
}

View File

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Services\Bunq\Object;
use FireflyIII\Exceptions\FireflyException;
/**
* Class LabelUser
*/
@ -83,6 +83,6 @@ class LabelUser extends BunqObject
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
throw new FireflyException(sprintf('Cannot convert %s to array.', \get_class($this)));
}
}

View File

@ -94,8 +94,8 @@ class MonetaryAccountBank extends BunqObject
$this->setting = new MonetaryAccountSetting($data['setting']);
$this->overdraftLimit = new Amount($data['overdraft_limit']);
$this->avatar = new Avatar($data['avatar']);
$this->reason = $data['reason'];
$this->reasonDescription = $data['reason_description'];
$this->reason = $data['reason'] ?? '';
$this->reasonDescription = $data['reason_description'] ?? '';
// create aliases:
foreach ($data['alias'] as $alias) {

View File

@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Services\Bunq\Object;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
/**
* Class Payment
@ -138,7 +138,7 @@ class Payment extends BunqObject
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
throw new FireflyException(sprintf('Cannot convert %s to array.', \get_class($this)));
}
}

View File

@ -61,6 +61,8 @@ class ServerPublicKey extends BunqObject
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
return [
'server_public_key' => $this->publicKey,
];
}
}

View File

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Services\Bunq\Object;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
/**
* Class UserLight.
*/
@ -81,6 +81,6 @@ class UserLight extends BunqObject
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
throw new FireflyException(sprintf('Cannot convert %s to array.', \get_class($this)));
}
}

View File

@ -58,8 +58,6 @@ class InstallationTokenRequest extends BunqRequest
Log::debug(sprintf('Installation ID: %s', $this->installationId->getId()));
Log::debug(sprintf('Installation token: %s', $this->installationToken->getToken()));
Log::debug('Server public key: (not included)');
return;
}
/**

View File

@ -103,7 +103,5 @@ class BunqToken
$this->created = Carbon::createFromFormat('Y-m-d H:i:s.u', $response['created']);
$this->updated = Carbon::createFromFormat('Y-m-d H:i:s.u', $response['updated']);
$this->token = $response['token'];
return;
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* IPRetrievalInterface.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Services\IP;
/**
* Interface IPRetrievalInterface
*
* @package FireflyIII\Services\IP
*/
interface IPRetrievalInterface
{
/**
* Returns the user's IP address.
*
* @return null|string
*/
public function getIP(): ?string;
}

View File

@ -0,0 +1,60 @@
<?php
/**
* IpifyOrg.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Services\IP;
use Exception;
use Log;
use Requests;
/**
* Class IpifyOrg
*/
class IpifyOrg implements IPRetrievalInterface
{
/**
* Returns the user's IP address.
*
* @noinspection MultipleReturnStatementsInspection
* @return null|string
*/
public function getIP(): ?string
{
$result = null;
try {
$response = Requests::get('https://api.ipify.org');
} catch (Exception $e) {
Log::warning(sprintf('The ipify.org service could not retrieve external IP: %s', $e->getMessage()));
Log::warning($e->getTraceAsString());
return null;
}
if (200 !== $response->status_code) {
Log::warning(sprintf('Could not retrieve external IP: %d %s', $response->status_code, $response->body));
return null;
}
return (string)$response->body;
}
}

View File

@ -208,7 +208,7 @@ class Preferences
/**
* @param \FireflyIII\User $user
* @param $name
* @param string $value
* @param mixed $value
*
* @return Preference
*/

View File

@ -38,6 +38,7 @@ return [
'journal_currency_id' => 'Currency',
'currency_id' => 'Currency',
'transaction_currency_id' => 'Currency',
'external_ip' => 'Your server\'s external IP',
'attachments' => 'Attachments',
'journal_amount' => 'Amount',
'journal_source_account_name' => 'Revenue account (source)',

View File

@ -164,6 +164,7 @@ return [
// bunq
'bunq_prerequisites_title' => 'Prerequisites for an import from bunq',
'bunq_prerequisites_text' => 'In order to import from bunq, you need to obtain an API key. You can do this through the app. Please note that the import function for bunq is in BETA. It has only been tested against the sandbox API.',
'bunq_prerequisites_text_ip' => 'Bunq requires your externally facing IP address. Firefly III has tried to fill this in using <a href="https://www.ipify.org/">the ipify service</a>. Make sure this IP address is correct, or the import will fail.',
'bunq_do_import' => 'Yes, import from this account',
'bunq_accounts_title' => 'Bunq accounts',
'bunq_accounts_text' => 'These are the accounts associated with your bunq account. Please select the accounts from which you want to import, and in which account the transactions must be imported.',

View File

@ -26,6 +26,20 @@
{{ ExpandedForm.text('api_key', key) }}
</div>
</div>
<div class="row">
<div class="col-lg-8">
<p>
{{ trans('import.bunq_prerequisites_text_ip')|raw }}
</p>
</div>
</div>
<div class="row">
<div class="col-lg-8">
{{ ExpandedForm.text('external_ip', ip) }}
</div>
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn pull-right btn-success">