From c8b710b3fd46fdb72887d67c54c731a1e1883f4f Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Wed, 31 Jan 2024 22:42:59 -0300 Subject: [PATCH 01/42] Split identify and sign methods Signed-off-by: Vitor Mattos --- lib/Helper/ValidateHelper.php | 4 +- .../IdentifyMethod/AbstractIdentifyMethod.php | 2 +- lib/Service/IdentifyMethod/Account.php | 3 +- lib/Service/IdentifyMethod/Email.php | 5 +- .../IdentifyMethod/IIdentifyMethod.php | 2 +- .../AbstractSignatureMethod.php | 30 ++++++ .../{ => SignatureMethod}/ClickToSign.php | 4 +- .../SignatureMethod/EmailToken.php | 97 +++++++++++++++++++ .../{ => SignatureMethod}/Password.php | 6 +- lib/Service/SignatureMethodService.php | 8 +- 10 files changed, 143 insertions(+), 18 deletions(-) create mode 100644 lib/Service/IdentifyMethod/SignatureMethod/AbstractSignatureMethod.php rename lib/Service/IdentifyMethod/{ => SignatureMethod}/ClickToSign.php (95%) create mode 100644 lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php rename lib/Service/IdentifyMethod/{ => SignatureMethod}/Password.php (95%) diff --git a/lib/Helper/ValidateHelper.php b/lib/Helper/ValidateHelper.php index 5f6ee2e363..92ffcc4e15 100644 --- a/lib/Helper/ValidateHelper.php +++ b/lib/Helper/ValidateHelper.php @@ -644,7 +644,7 @@ private function validateIdentifyMethod(string $uuid, ?IUser $user = null): void foreach ($identifyMethods as $methods) { foreach ($methods as $identifyMethod) { $identifyMethod->setUser($user); - $identifyMethod->validateToSign(); + $identifyMethod->validateToIdentify(); } } } @@ -737,7 +737,7 @@ public function validateCredentials(SignRequest $signRequest, IUser $user, strin } $identifyMethod->setUser($user); $identifyMethod->setCodeSentByUser($token); - $identifyMethod->validateToSign(); + $identifyMethod->validateToIdentify(); } public function validateIfIdentifyMethodExists($identifyMethod): void { diff --git a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php index 99cccff902..69e6172838 100644 --- a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php @@ -122,7 +122,7 @@ public function validateToRequest(): void { public function validateToCreateAccount(string $value): void { } - public function validateToSign(): void { + public function validateToIdentify(): void { } protected function throwIfFileNotFound(): void { diff --git a/lib/Service/IdentifyMethod/Account.php b/lib/Service/IdentifyMethod/Account.php index 488cba5f98..41d4f0dcd9 100644 --- a/lib/Service/IdentifyMethod/Account.php +++ b/lib/Service/IdentifyMethod/Account.php @@ -46,7 +46,6 @@ use Psr\Log\LoggerInterface; class Account extends AbstractIdentifyMethod { - public const ID = 'account'; public function __construct( private IAppConfig $appConfig, private IL10N $l10n, @@ -104,7 +103,7 @@ public function validateToRequest(): void { } } - public function validateToSign(): void { + public function validateToIdentify(): void { $signer = $this->getSigner(); $this->throwIfNotAuthenticated($this->user); $this->authenticatedUserIsTheSigner($this->user, $signer); diff --git a/lib/Service/IdentifyMethod/Email.php b/lib/Service/IdentifyMethod/Email.php index 118f876455..5258f66573 100644 --- a/lib/Service/IdentifyMethod/Email.php +++ b/lib/Service/IdentifyMethod/Email.php @@ -44,7 +44,6 @@ use Psr\Log\LoggerInterface; class Email extends AbstractIdentifyMethod { - public const ID = 'email'; public function __construct( private IAppConfig $appConfig, private IL10N $l10n, @@ -63,7 +62,7 @@ public function __construct( private FileElementMapper $fileElementMapper, ) { // TRANSLATORS Name of possible authenticator method. This signalize that the signer could be identified by email - $this->friendlyName = $this->l10n->t('Email token'); + $this->friendlyName = $this->l10n->t('Email'); parent::__construct( $appConfig, $l10n, @@ -98,7 +97,7 @@ public function validateToRequest(): void { $this->throwIfInvalidEmail(); } - public function validateToSign(): void { + public function validateToIdentify(): void { $this->throwIfAccountAlreadyExists($this->user); $this->throwIfIsAuthenticatedWithDifferentAccount($this->user); $this->throwIfInvalidToken(); diff --git a/lib/Service/IdentifyMethod/IIdentifyMethod.php b/lib/Service/IdentifyMethod/IIdentifyMethod.php index 5bd52aa851..ffe2be870e 100644 --- a/lib/Service/IdentifyMethod/IIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/IIdentifyMethod.php @@ -40,7 +40,7 @@ public function willNotifyUser(bool $willNotify): void; public function notify(bool $isNew): void; public function validateToRequest(): void; public function validateToCreateAccount(string $value): void; - public function validateToSign(): void; + public function validateToIdentify(): void; public function validateToRenew(?IUser $user = null): void; public function save(): void; public function delete(): void; diff --git a/lib/Service/IdentifyMethod/SignatureMethod/AbstractSignatureMethod.php b/lib/Service/IdentifyMethod/SignatureMethod/AbstractSignatureMethod.php new file mode 100644 index 0000000000..e03eeacb78 --- /dev/null +++ b/lib/Service/IdentifyMethod/SignatureMethod/AbstractSignatureMethod.php @@ -0,0 +1,30 @@ + + * + * @author Vitor Mattos + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; + +use OCA\Libresign\Service\IdentifyMethod\AbstractIdentifyMethod; + +abstract class AbstractSignatureMethod extends AbstractIdentifyMethod { +} diff --git a/lib/Service/IdentifyMethod/ClickToSign.php b/lib/Service/IdentifyMethod/SignatureMethod/ClickToSign.php similarity index 95% rename from lib/Service/IdentifyMethod/ClickToSign.php rename to lib/Service/IdentifyMethod/SignatureMethod/ClickToSign.php index c7a326d753..5cedbb949b 100644 --- a/lib/Service/IdentifyMethod/ClickToSign.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/ClickToSign.php @@ -22,7 +22,7 @@ * along with this program. If not, see . */ -namespace OCA\Libresign\Service\IdentifyMethod; +namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; use OCA\Libresign\Db\FileMapper; use OCA\Libresign\Db\IdentifyMethodMapper; @@ -40,7 +40,7 @@ use OCP\Security\IHasher; use Psr\Log\LoggerInterface; -class ClickToSign extends AbstractIdentifyMethod { +class ClickToSign extends AbstractSignatureMethod { public function __construct( private IAppConfig $appConfig, private IL10N $l10n, diff --git a/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php new file mode 100644 index 0000000000..f46b5b64dc --- /dev/null +++ b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php @@ -0,0 +1,97 @@ + + * + * @author Vitor Mattos + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; + +use OCA\Libresign\Db\FileElementMapper; +use OCA\Libresign\Db\FileMapper; +use OCA\Libresign\Db\IdentifyMethodMapper; +use OCA\Libresign\Db\SignRequestMapper; +use OCA\Libresign\Service\MailService; +use OCA\Libresign\Service\SessionService; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\Config\IUserMountCache; +use OCP\Files\IRootFolder; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\IUserManager; +use OCP\Security\IHasher; +use Psr\Log\LoggerInterface; + +class EmailToken extends AbstractSignatureMethod { + public const ID = 'email'; + public function __construct( + private IConfig $config, + private IL10N $l10n, + private MailService $mail, + private SignRequestMapper $signRequestMapper, + private IdentifyMethodMapper $identifyMethodMapper, + private FileMapper $fileMapper, + private IUserManager $userManager, + private IURLGenerator $urlGenerator, + private IRootFolder $root, + private IHasher $hasher, + private IUserMountCache $userMountCache, + private ITimeFactory $timeFactory, + private LoggerInterface $logger, + private SessionService $sessionService, + private FileElementMapper $fileElementMapper, + ) { + // TRANSLATORS Name of possible authenticator method. This signalize that the signer could be identified by email + $this->friendlyName = $this->l10n->t('Email token'); + parent::__construct( + $config, + $l10n, + $identifyMethodMapper, + $signRequestMapper, + $fileMapper, + $root, + $hasher, + $userManager, + $urlGenerator, + $userMountCache, + $timeFactory, + $logger, + $sessionService, + ); + $this->getSettings(); + } + + public function getSettings(): array { + if (!empty($this->settings)) { + return $this->settings; + } + $this->settings = parent::getSettingsFromDatabase( + default: [ + 'enabled' => false, + ], + immutable: [ + 'test_url' => $this->urlGenerator->linkToRoute('settings.MailSettings.sendTestMail'), + ] + ); + $this->canCreateAccount = $this->settings['can_create_account']; + return $this->settings; + } +} diff --git a/lib/Service/IdentifyMethod/Password.php b/lib/Service/IdentifyMethod/SignatureMethod/Password.php similarity index 95% rename from lib/Service/IdentifyMethod/Password.php rename to lib/Service/IdentifyMethod/SignatureMethod/Password.php index 23a49a6a91..2443e43747 100644 --- a/lib/Service/IdentifyMethod/Password.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/Password.php @@ -22,7 +22,7 @@ * along with this program. If not, see . */ -namespace OCA\Libresign\Service\IdentifyMethod; +namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; use OCA\Libresign\Db\FileMapper; use OCA\Libresign\Db\IdentifyMethodMapper; @@ -41,7 +41,7 @@ use OCP\Security\IHasher; use Psr\Log\LoggerInterface; -class Password extends AbstractIdentifyMethod { +class Password extends AbstractSignatureMethod { public const ID = 'password'; public function __construct( private IAppConfig $appConfig, @@ -79,7 +79,7 @@ public function __construct( ); } - public function validateToSign(): void { + public function validateToIdentify(): void { $pfx = $this->pkcs12Handler->getPfx($this->user->getUID()); openssl_pkcs12_read($pfx, $cert_info, $this->getEntity()->getIdentifierValue()); if (empty($cert_info)) { diff --git a/lib/Service/SignatureMethodService.php b/lib/Service/SignatureMethodService.php index c27c6b448a..7e516fd140 100644 --- a/lib/Service/SignatureMethodService.php +++ b/lib/Service/SignatureMethodService.php @@ -27,10 +27,10 @@ use OCA\Libresign\Db\SignRequest; use OCA\Libresign\Exception\LibresignException; use OCA\Libresign\Service\IdentifyMethod\AbstractIdentifyMethod; -use OCA\Libresign\Service\IdentifyMethod\ClickToSign; -use OCA\Libresign\Service\IdentifyMethod\Email; use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod; -use OCA\Libresign\Service\IdentifyMethod\Password; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\ClickToSign; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\EmailToken; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\Password; use OCP\Accounts\IAccountManager; use OCP\App\IAppManager; use OCP\AppFramework\OCS\OCSForbiddenException; @@ -64,7 +64,7 @@ public function __construct( private MailService $mail, private Password $password, private ClickToSign $clickToSign, - private Email $email, + private EmailToken $email, ) { $this->methods = [ $this->password->getName() => $this->password, From a243d4bdac0dde7328bee0010dc543649872b8f7 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Wed, 31 Jan 2024 23:13:42 -0300 Subject: [PATCH 02/42] Remove unecessary setting Signed-off-by: Vitor Mattos --- lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php index f46b5b64dc..318d960b6d 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php @@ -91,7 +91,6 @@ public function getSettings(): array { 'test_url' => $this->urlGenerator->linkToRoute('settings.MailSettings.sendTestMail'), ] ); - $this->canCreateAccount = $this->settings['can_create_account']; return $this->settings; } } From 81f276bca7471282c6cdaf55c380ddef820d0ad2 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Thu, 1 Feb 2024 14:17:24 -0300 Subject: [PATCH 03/42] Move signature methods to each identify factor Signed-off-by: Vitor Mattos --- src/views/Settings/IdentifierFactor.vue | 121 +++++++++++++----------- src/views/Settings/Settings.vue | 3 - src/views/Settings/SignatureMethods.vue | 53 ----------- 3 files changed, 67 insertions(+), 110 deletions(-) delete mode 100644 src/views/Settings/SignatureMethods.vue diff --git a/src/views/Settings/IdentifierFactor.vue b/src/views/Settings/IdentifierFactor.vue index fad943d3ac..754bc1e3b0 100644 --- a/src/views/Settings/IdentifierFactor.vue +++ b/src/views/Settings/IdentifierFactor.vue @@ -3,46 +3,34 @@

-
- - {{ option.friendly_name }} - -
- - {{ t('libresign', 'Make this method required') }} - -
-
-
- - {{ option.friendly_name }} - -
- + {{ option.friendly_name }} + +
+
+ {{ t('libresign', 'Request to create account when the user does not have an account') }} - -
-
-
- - {{ option.friendly_name }} - -
-
- - {{ t('libresign', 'Make this method required') }} - -
-
+ + +
+ + {{ t('libresign', 'Make this method required') }} + +
+
+ {{ t('libresign', 'Signature methods') }} + + {{ method.label }} + +
@@ -51,7 +39,6 @@ import { translate as t } from '@nextcloud/l10n' import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js' import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js' -import NcActionCheckbox from '@nextcloud/vue/dist/Components/NcActionCheckbox.js' import { loadState } from '@nextcloud/initial-state' export default { @@ -59,7 +46,6 @@ export default { components: { NcSettingsSection, NcCheckboxRadioSwitch, - NcActionCheckbox, }, data() { const identifyMethod = loadState('libresign', 'identify_methods') @@ -73,6 +59,7 @@ export default { }, mounted() { this.flagAccountIfAllDisabled() + this.flagFirstSignatureMethodIfAllDisabled() }, methods: { flagAccountIfAllDisabled() { @@ -86,33 +73,59 @@ export default { .enabled = true } }, + flagFirstSignatureMethodIfAllDisabled() { + this.options.forEach(item => { + const allDisabled = Object.values(item.signatureMethods) + .filter(item => item.enabled) + .length === 0 + if (allDisabled) { + // Enable the first signature method + Object.keys(item.signatureMethods).every(methodId => { + item.signatureMethods[methodId].enabled = true + return false + }) + } + }) + }, save() { this.flagAccountIfAllDisabled() + this.flagFirstSignatureMethodIfAllDisabled() + // Get only enabled + let props = this.options.filter(item => item.enabled) + // Save only enabled property, removing other properties + props = JSON.parse(JSON.stringify(props)) + .map(item => { + Object.keys(item.signatureMethods).forEach(id => { + item.signatureMethods[id] = { + enabled: item.signatureMethods[id].enabled, + } + }) + return item + }) OCP.AppConfig.setValue('libresign', 'identify_methods', - JSON.stringify( - this.options.filter(item => item.enabled), - ), + JSON.stringify(props), ) }, }, } diff --git a/src/views/Settings/Settings.vue b/src/views/Settings/Settings.vue index 4910c37d5b..ab7616b964 100644 --- a/src/views/Settings/Settings.vue +++ b/src/views/Settings/Settings.vue @@ -29,7 +29,6 @@ - @@ -55,7 +54,6 @@ import IdentificationDocuments from './IdentificationDocuments.vue' import CollectMetadata from './CollectMetadata.vue' import DefaultUserFolder from './DefaultUserFolder.vue' import IdentifierFactor from './IdentifierFactor.vue' -import SignatureMethods from './SignatureMethods.vue' export default { name: 'Settings', @@ -67,7 +65,6 @@ export default { RootCertificateCfssl, RootCertificateOpenSsl, IdentifierFactor, - SignatureMethods, ExpirationRules, Validation, AllowedGroups, diff --git a/src/views/Settings/SignatureMethods.vue b/src/views/Settings/SignatureMethods.vue deleted file mode 100644 index d24836b2e0..0000000000 --- a/src/views/Settings/SignatureMethods.vue +++ /dev/null @@ -1,53 +0,0 @@ - - From 2a4422045f0a780693f9b2a5afdec44c3ff67e92 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Thu, 1 Feb 2024 14:18:17 -0300 Subject: [PATCH 04/42] Allow to get default values recursive Signed-off-by: Vitor Mattos --- lib/Service/IdentifyMethod/AbstractIdentifyMethod.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php index 69e6172838..0a9298a73e 100644 --- a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php @@ -333,8 +333,12 @@ private function getSavedSignatureMethodsSettings(): array { private function getDefaultValues(array $customConfig, array $default): array { foreach ($default as $key => $value) { - if (!isset($customConfig[$key]) || gettype($value) !== gettype($customConfig[$key])) { + if (!isset($customConfig[$key])) { $customConfig[$key] = $value; + } elseif (gettype($value) !== gettype($customConfig[$key])) { + $customConfig[$key] = $value; + } elseif(gettype($value) === 'array') { + $customConfig[$key] = $this->getDefaultValues($customConfig[$key], $value); } } return $customConfig; From 27a36bebc8b99617c8c706a0f621e312e249d440 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 2 Feb 2024 14:13:05 -0300 Subject: [PATCH 05/42] Bump dependencies Signed-off-by: Vitor Mattos --- composer.lock | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/composer.lock b/composer.lock index 69aa85138e..172ee3934c 100644 --- a/composer.lock +++ b/composer.lock @@ -1756,12 +1756,12 @@ "source": { "type": "git", "url": "https://github.com/nextcloud-deps/ocp.git", - "reference": "6a4c892db2749bf19aa3b83a4c2673a2302fa46e" + "reference": "bdcadd5d25136be604f48c963109a56e2b478b27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/6a4c892db2749bf19aa3b83a4c2673a2302fa46e", - "reference": "6a4c892db2749bf19aa3b83a4c2673a2302fa46e", + "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/bdcadd5d25136be604f48c963109a56e2b478b27", + "reference": "bdcadd5d25136be604f48c963109a56e2b478b27", "shasum": "" }, "require": { @@ -1793,7 +1793,7 @@ "issues": "https://github.com/nextcloud-deps/ocp/issues", "source": "https://github.com/nextcloud-deps/ocp/tree/master" }, - "time": "2024-02-01T00:33:54+00:00" + "time": "2024-02-02T00:32:33+00:00" }, { "name": "psr/clock", @@ -1949,12 +1949,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "e3b3cce1f2454ee4575500e084211b11efdaf64b" + "reference": "2f3b470e6ca356a27bf10b2b439c3683d20bebc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/e3b3cce1f2454ee4575500e084211b11efdaf64b", - "reference": "e3b3cce1f2454ee4575500e084211b11efdaf64b", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/2f3b470e6ca356a27bf10b2b439c3683d20bebc1", + "reference": "2f3b470e6ca356a27bf10b2b439c3683d20bebc1", "shasum": "" }, "conflict": { @@ -2008,6 +2008,7 @@ "bolt/bolt": "<3.7.2", "bolt/core": "<=4.2", "bottelet/flarepoint": "<2.2.1", + "bref/bref": "<2.1.13", "brightlocal/phpwhois": "<=4.2.5", "brotkrueml/codehighlight": "<2.7", "brotkrueml/schema": "<1.13.1|>=2,<2.5.1", @@ -2238,7 +2239,7 @@ "liftkit/database": "<2.13.2", "limesurvey/limesurvey": "<3.27.19", "livehelperchat/livehelperchat": "<=3.91", - "livewire/livewire": ">2.2.4,<2.2.6", + "livewire/livewire": ">2.2.4,<2.2.6|>=3,<3.0.4", "lms/routes": "<2.1.1", "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", "luyadev/yii-helpers": "<1.2.1", @@ -2254,7 +2255,7 @@ "marcwillmann/turn": "<0.3.3", "matyhtf/framework": "<3.0.6", "mautic/core": "<4.3", - "mediawiki/core": ">=1.27,<1.27.6|>=1.29,<1.29.3|>=1.30,<1.30.2|>=1.31,<1.31.9|>=1.32,<1.32.6|>=1.32.99,<1.33.3|>=1.33.99,<1.34.3|>=1.34.99,<1.35", + "mediawiki/core": "<1.36.2", "mediawiki/matomo": "<2.4.3", "mediawiki/semantic-media-wiki": "<4.0.2", "melisplatform/melis-asset-manager": "<5.0.1", @@ -2457,7 +2458,7 @@ "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", "ssddanbrown/bookstack": "<22.02.3", - "statamic/cms": "<4.36", + "statamic/cms": "<4.46", "stormpath/sdk": "<9.9.99", "studio-42/elfinder": "<2.1.62", "subhh/libconnect": "<7.0.8|>=8,<8.1", @@ -2673,7 +2674,7 @@ "type": "tidelift" } ], - "time": "2024-01-31T19:04:19+00:00" + "time": "2024-02-02T13:04:17+00:00" } ], "aliases": [], From cd5d08f44c5386bdc6235f60b52f3c06d2a3e128 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 2 Feb 2024 15:08:29 -0300 Subject: [PATCH 06/42] Add stubs to psalm Signed-off-by: Vitor Mattos --- psalm.xml | 3 ++ tests/psalm-baseline.xml | 85 -------------------------------- tests/stubs/oc_hooks_emitter.php | 11 +++++ 3 files changed, 14 insertions(+), 85 deletions(-) create mode 100644 tests/stubs/oc_hooks_emitter.php diff --git a/psalm.xml b/psalm.xml index c7e6af85c3..6e8a7d648b 100644 --- a/psalm.xml +++ b/psalm.xml @@ -32,4 +32,7 @@ + + + diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml index 3519bf9db0..22fb8bfabb 100644 --- a/tests/psalm-baseline.xml +++ b/tests/psalm-baseline.xml @@ -55,18 +55,6 @@ LoadSidebar - - - $this->root - $this->root - $this->root - $this->root - $this->root - $this->root - $this->root - private - - LoadSidebar @@ -77,26 +65,12 @@ ViewController - - - $this->root - IRootFolder - IRootFolder - - $schema->dropTable('libresign_file_user') - - $this->root - $this->root - $this->root - $this->root - private - $this->newUserMail $this->newUserMail @@ -104,55 +78,8 @@ UUIDUtil private - - \OCA\Files\Node\File - - - - - private - - - - - $this->root - $this->root - $this->root - $this->root - private - - - - - $this->root - $this->root - private - - - - - private - - - - - private - - - - - private - - - - - private - - - private - Process @@ -164,18 +91,6 @@ - - $this->root - $this->root - $this->root - $this->root - $this->root - $this->root - $this->root - $this->root - $this->root - private - UUIDUtil UUIDUtil diff --git a/tests/stubs/oc_hooks_emitter.php b/tests/stubs/oc_hooks_emitter.php new file mode 100644 index 0000000000..ed72d9c3aa --- /dev/null +++ b/tests/stubs/oc_hooks_emitter.php @@ -0,0 +1,11 @@ + Date: Fri, 2 Feb 2024 15:08:53 -0300 Subject: [PATCH 07/42] Fixes after changes at psalm Signed-off-by: Vitor Mattos --- lib/Helper/ValidateHelper.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/Helper/ValidateHelper.php b/lib/Helper/ValidateHelper.php index 92ffcc4e15..6a4c23742d 100644 --- a/lib/Helper/ValidateHelper.php +++ b/lib/Helper/ValidateHelper.php @@ -54,7 +54,7 @@ use OCP\Security\IHasher; class ValidateHelper { - /** @var \OCP\Files\File[] */ + /** @var \OCP\Files\Node[] */ private $file = []; public const TYPE_TO_SIGN = 1; @@ -429,11 +429,12 @@ private function getLibreSignFileByNodeId(int $nodeId) { $libresignFile = $this->fileMapper->getByFileId($nodeId); $userFolder = $this->root->getUserFolder($libresignFile->getUserId()); - $this->file[$nodeId] = $userFolder->getById($nodeId); - if (!empty($this->file[$nodeId])) { - $this->file[$nodeId] = $this->file[$nodeId][0]; + $files = $userFolder->getById($nodeId); + if (!empty($files)) { + $this->file[$nodeId] = $files[0]; + return $this->file[$nodeId]; } - return $this->file[$nodeId]; + return []; } public function canRequestSign(IUser $user): void { From 3fd05e21f7e0f794c369f91e8b12079ac347acda Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 2 Feb 2024 15:09:31 -0300 Subject: [PATCH 08/42] Update to php 8.0 Signed-off-by: Vitor Mattos --- lib/Service/AccountService.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/Service/AccountService.php b/lib/Service/AccountService.php index 400e296aa4..7bad2cd8ee 100644 --- a/lib/Service/AccountService.php +++ b/lib/Service/AccountService.php @@ -62,12 +62,9 @@ use Throwable; class AccountService { - /** @var SignRequest */ - private $signRequest; - /** @var \OCA\Libresign\Db\File */ - private $fileData; - /** @var \OCA\Files\Node\File */ - private $fileToSign; + private SignRequest $signRequest; + private \OCA\Libresign\Db\File $fileData; + private \OCP\Files\File $fileToSign; public const ELEMENT_SIGN_WIDTH = 350; public const ELEMENT_SIGN_HEIGHT = 100; From 5b8071a8101d90ab60448f64e216e27b88f7df77 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 2 Feb 2024 15:33:37 -0300 Subject: [PATCH 09/42] Start to split identify methods Signed-off-by: Vitor Mattos --- .../IdentifyMethod/AbstractIdentifyMethod.php | 211 ++++++------------ lib/Service/IdentifyMethod/Account.php | 54 ++--- lib/Service/IdentifyMethod/Email.php | 69 ++---- .../IdentifyMethod/IIdentifyMethod.php | 1 + .../IdentifyMethod/IdentifyMethodService.php | 145 ++++++++++++ .../SignatureMethod/ClickToSign.php | 48 +--- .../SignatureMethod/EmailToken.php | 64 +----- .../SignatureMethod/Password.php | 80 +------ 8 files changed, 280 insertions(+), 392 deletions(-) create mode 100644 lib/Service/IdentifyMethod/IdentifyMethodService.php diff --git a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php index 0a9298a73e..1498e92b92 100644 --- a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php @@ -26,23 +26,12 @@ use InvalidArgumentException; use OCA\Libresign\Db\File as FileEntity; -use OCA\Libresign\Db\FileMapper; use OCA\Libresign\Db\IdentifyMethod; -use OCA\Libresign\Db\IdentifyMethodMapper; -use OCA\Libresign\Db\SignRequestMapper; use OCA\Libresign\Exception\LibresignException; use OCA\Libresign\Helper\JSActions; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\AbstractSignatureMethod; use OCA\Libresign\Service\SessionService; -use OCP\AppFramework\Services\IAppConfig; -use OCP\AppFramework\Utility\ITimeFactory; -use OCP\Files\Config\IUserMountCache; -use OCP\Files\IRootFolder; -use OCP\IL10N; -use OCP\IURLGenerator; use OCP\IUser; -use OCP\IUserManager; -use OCP\Security\IHasher; -use Psr\Log\LoggerInterface; use Wobeto\EmailBlur\Blur; abstract class AbstractIdentifyMethod implements IIdentifyMethod { @@ -54,20 +43,12 @@ abstract class AbstractIdentifyMethod implements IIdentifyMethod { protected string $codeSentByUser = ''; protected array $settings = []; protected bool $willNotify = true; + /** + * @var AbstractSignatureMethod[] + */ + protected array $signatureMethods = []; public function __construct( - private IAppConfig $appConfig, - private IL10N $l10n, - private IdentifyMethodMapper $identifyMethodMapper, - private SignRequestMapper $signRequestMapper, - private FileMapper $fileMapper, - private IRootFolder $root, - private IHasher $hasher, - private IUserManager $userManager, - private IURLGenerator $urlGenerator, - private IUserMountCache $userMountCache, - private ITimeFactory $timeFactory, - private LoggerInterface $logger, - private SessionService $sessionService, + protected IdentifyMethodService $identifyMethodService, ) { $className = (new \ReflectionClass($this))->getShortName(); $this->name = lcfirst($className); @@ -104,6 +85,15 @@ public function getEntity(): IdentifyMethod { return $this->entity; } + public function getSignatureMethods(): array { + return array_map(function (AbstractSignatureMethod $method) { + return [ + 'label' => $method->friendlyName, + 'enabled' => $method->isEnabledAsSignatueMethod(), + ]; + }, $this->signatureMethods); + } + public function getSettings(): array { $this->getSettingsFromDatabase(); return $this->settings; @@ -126,35 +116,35 @@ public function validateToIdentify(): void { } protected function throwIfFileNotFound(): void { - $signRequest = $this->signRequestMapper->getById($this->getEntity()->getSignRequestId()); - $fileEntity = $this->fileMapper->getById($signRequest->getFileId()); + $signRequest = $this->identifyMethodService->getSignRequestMapper()->getById($this->getEntity()->getSignRequestId()); + $fileEntity = $this->identifyMethodService->getFileMapper()->getById($signRequest->getFileId()); $nodeId = $fileEntity->getNodeId(); - $mountsContainingFile = $this->userMountCache->getMountsForFileId($nodeId); + $mountsContainingFile = $this->identifyMethodService->getUserMountCache()->getMountsForFileId($nodeId); foreach ($mountsContainingFile as $fileInfo) { - $this->root->getByIdInPath($nodeId, $fileInfo->getMountPoint()); + $this->identifyMethodService->getRootFolder()->getByIdInPath($nodeId, $fileInfo->getMountPoint()); } - $fileToSign = $this->root->getById($nodeId); + $fileToSign = $this->identifyMethodService->getRootFolder()->getById($nodeId); if (count($fileToSign) < 1) { throw new LibresignException(json_encode([ 'action' => JSActions::ACTION_DO_NOTHING, - 'errors' => [$this->l10n->t('File not found')], + 'errors' => [$this->identifyMethodService->getL10n()->t('File not found')], ])); } } protected function throwIfMaximumValidityExpired(): void { - $maximumValidity = (int) $this->appConfig->getAppValue('maximum_validity', (string) SessionService::NO_MAXIMUM_VALIDITY); + $maximumValidity = (int) $this->identifyMethodService->getAppConfig()->getAppValue('maximum_validity', (string) SessionService::NO_MAXIMUM_VALIDITY); if ($maximumValidity <= 0) { return; } - $signRequest = $this->signRequestMapper->getById($this->getEntity()->getSignRequestId()); - $now = $this->timeFactory->getTime(); + $signRequest = $this->identifyMethodService->getSignRequestMapper()->getById($this->getEntity()->getSignRequestId()); + $now = $this->identifyMethodService->getTimeFactory()->getTime(); if ($signRequest->getCreatedAt() + $maximumValidity < $now) { throw new LibresignException(json_encode([ 'action' => JSActions::ACTION_DO_NOTHING, - 'errors' => [$this->l10n->t('Link expired.')], + 'errors' => [$this->identifyMethodService->getL10n()->t('Link expired.')], ])); } } @@ -163,36 +153,37 @@ protected function throwIfInvalidToken(): void { if (empty($this->codeSentByUser)) { return; } - if (!$this->hasher->verify($this->codeSentByUser, $this->getEntity()->getCode())) { - throw new LibresignException($this->l10n->t('Invalid code.')); + if (!$this->identifyMethodService->getHasher()->verify($this->codeSentByUser, $this->getEntity()->getCode())) { + throw new LibresignException($this->identifyMethodService->getL10n()->t('Invalid code.')); } } protected function renewSession(): void { - $this->sessionService->setIdentifyMethodId($this->getEntity()->getId()); - $renewalInterval = (int) $this->appConfig->getAppValue('renewal_interval', (string) SessionService::NO_RENEWAL_INTERVAL); + $this->identifyMethodService->getSessionService()->setIdentifyMethodId($this->getEntity()->getId()); + $renewalInterval = (int) $this->identifyMethodService->getAppConfig()->getAppValue('renewal_interval', (string) SessionService::NO_RENEWAL_INTERVAL); if ($renewalInterval <= 0) { return; } - $this->sessionService->resetDurationOfSignPage(); + $this->identifyMethodService->getSessionService()->resetDurationOfSignPage(); } protected function updateIdentifiedAt(): void { if ($this->getEntity()->getCode() && !$this->getEntity()->getIdentifiedAtDate()) { return; } - $this->getEntity()->setIdentifiedAtDate($this->timeFactory->getDateTime()); + $this->getEntity()->setIdentifiedAtDate($this->identifyMethodService->getTimeFactory()->getDateTime()); $this->willNotify = false; - $this->save(); + $isNew = $this->identifyMethodService->save($this->getEntity()); + $this->notify($isNew); } protected function throwIfRenewalIntervalExpired(): void { - $renewalInterval = (int) $this->appConfig->getAppValue('renewal_interval', (string) SessionService::NO_RENEWAL_INTERVAL); + $renewalInterval = (int) $this->identifyMethodService->getAppConfig()->getAppValue('renewal_interval', (string) SessionService::NO_RENEWAL_INTERVAL); if ($renewalInterval <= 0) { return; } - $signRequest = $this->signRequestMapper->getById($this->getEntity()->getSignRequestId()); - $startTime = $this->sessionService->getSignStartTime(); + $signRequest = $this->identifyMethodService->getSignRequestMapper()->getById($this->getEntity()->getSignRequestId()); + $startTime = $this->identifyMethodService->getSessionService()->getSignStartTime(); $createdAt = $signRequest->getCreatedAt(); $lastAttempt = $this->getEntity()->getLastAttemptDate()?->format('U'); $lastActionDate = max( @@ -200,8 +191,8 @@ protected function throwIfRenewalIntervalExpired(): void { $createdAt, $lastAttempt, ); - $now = $this->timeFactory->getTime(); - $this->logger->debug('AbstractIdentifyMethod::throwIfRenewalIntervalExpired Times', [ + $now = $this->identifyMethodService->getTimeFactory()->getTime(); + $this->identifyMethodService->getLogger()->debug('AbstractIdentifyMethod::throwIfRenewalIntervalExpired Times', [ 'renewalInterval' => $renewalInterval, 'startTime' => $startTime, 'createdAt' => $createdAt, @@ -210,13 +201,13 @@ protected function throwIfRenewalIntervalExpired(): void { 'now' => $now, ]); if ($lastActionDate + $renewalInterval < $now) { - $this->logger->debug('AbstractIdentifyMethod::throwIfRenewalIntervalExpired Exception'); + $this->identifyMethodService->getLogger()->debug('AbstractIdentifyMethod::throwIfRenewalIntervalExpired Exception'); $blur = new Blur($this->getEntity()->getIdentifierValue()); throw new LibresignException(json_encode([ 'action' => $this->getRenewAction(), // TRANSLATORS title that is displayed at screen to notify the signer that the link to sign the document expired - 'title' => $this->l10n->t('Link expired'), - 'body' => $this->l10n->t(<<<'BODY' + 'title' => $this->identifyMethodService->getL10n()->t('Link expired'), + 'body' => $this->identifyMethodService->getL10n()->t(<<<'BODY' The link to sign the document has expired. We will send a new link to the email %1$s. Click below to receive the new link and be able to sign the document. @@ -225,7 +216,7 @@ protected function throwIfRenewalIntervalExpired(): void { ), 'uuid' => $signRequest->getUuid(), // TRANSLATORS Button to renew the link to sign the document. Renew is the action to generate a new sign link when the link expired. - 'renewButton' => $this->l10n->t('Renew'), + 'renewButton' => $this->identifyMethodService->getL10n()->t('Renew'), ])); } } @@ -234,14 +225,14 @@ protected function throwIfNeedToCreateAccount() { if (!$this->canCreateAccount) { return; } - if ($this->sessionService->getSignStartTime()) { + if ($this->identifyMethodService->getSessionService()->getSignStartTime()) { return; } $email = $this->getEntity()->getIdentifierValue(); throw new LibresignException(json_encode([ 'action' => JSActions::ACTION_CREATE_USER, 'settings' => ['accountHash' => md5($email)], - 'message' => $this->l10n->t('You need to create an account to sign this file.'), + 'message' => $this->identifyMethodService->getL10n()->t('You need to create an account to sign this file.'), ])); } @@ -254,14 +245,14 @@ private function getRenewAction(): int { } protected function throwIfAlreadySigned(): void { - $signRequest = $this->signRequestMapper->getById($this->getEntity()->getSignRequestId()); - $fileEntity = $this->fileMapper->getById($signRequest->getFileId()); + $signRequest = $this->identifyMethodService->getSignRequestMapper()->getById($this->getEntity()->getSignRequestId()); + $fileEntity = $this->identifyMethodService->getFileMapper()->getById($signRequest->getFileId()); if ($fileEntity->getStatus() === FileEntity::STATUS_SIGNED || (!is_null($signRequest) && $signRequest->getSigned()) ) { throw new LibresignException(json_encode([ 'action' => JSActions::ACTION_SHOW_ERROR, - 'errors' => [$this->l10n->t('File already signed.')], + 'errors' => [$this->identifyMethodService->getL10n()->t('File already signed.')], ])); } } @@ -270,128 +261,74 @@ protected function getSettingsFromDatabase(array $default = [], array $immutable if ($this->settings) { return $this->settings; } + $this->loadSavedSettings(); $default = array_merge( [ 'name' => $this->name, 'friendly_name' => $this->friendlyName, 'enabled' => true, - 'enabled_as_signature_method' => false, 'mandatory' => true, + 'signatureMethods' => $this->getSignatureMethods(), ], $default ); - $customConfig = $this->getSavedSettings(); - $customConfig = $this->removeKeysThatDontExists($customConfig, $default); - $customConfig = $this->overrideImmutable($customConfig, $immutable); - $customConfig = $this->getDefaultValues($customConfig, $default); - $this->settings = $customConfig; + $this->removeKeysThatDontExists($default); + $this->overrideImmutable($immutable); + $this->settings = $this->applyDefault($this->settings, $default); return $this->settings; } - private function overrideImmutable(array $customConfig, array $immutable) { - return array_merge($customConfig, $immutable); + private function overrideImmutable(array $immutable): void { + $this->settings = array_merge($this->settings, $immutable); } - private function getSavedSettings(): array { - return array_merge( - $this->getSavedIdentifyMethodsSettings(), - $this->getSavedSignatureMethodsSettings(), - ); - } - - private function getSavedIdentifyMethodsSettings(): array { - $config = $this->appConfig->getAppValue('identify_methods', '[]'); + private function loadSavedSettings(): void { + $config = $this->identifyMethodService->getAppConfig()->getAppValue('identify_methods', '[]'); $config = json_decode($config, true); if (json_last_error() !== JSON_ERROR_NONE || !is_array($config)) { - return []; + $this->settings = []; + return; } - $current = array_reduce($config, function ($carry, $config) { + $this->settings = array_reduce($config, function ($carry, $config) { if ($config['name'] === $this->name) { return $config; } return $carry; }, []); - return $current; } - private function getSavedSignatureMethodsSettings(): array { - $config = $this->appConfig->getAppValue('signature_methods', '[]'); - $config = json_decode($config, true); - if (json_last_error() !== JSON_ERROR_NONE || !is_array($config)) { - return []; - } - foreach ($config as $id => $method) { - if ($id !== $this->name) { - continue; - } - return [ - 'enabled_as_signature_method' => array_key_exists('enabled', $method) && $method['enabled'], - ]; - } - return []; - } - - private function getDefaultValues(array $customConfig, array $default): array { + private function applyDefault(array $customConfig, array $default): array { foreach ($default as $key => $value) { if (!isset($customConfig[$key])) { $customConfig[$key] = $value; } elseif (gettype($value) !== gettype($customConfig[$key])) { $customConfig[$key] = $value; - } elseif(gettype($value) === 'array') { - $customConfig[$key] = $this->getDefaultValues($customConfig[$key], $value); + } elseif (gettype($value) === 'array') { + $customConfig[$key] = $this->applyDefault($customConfig[$key], $value); } } return $customConfig; } - private function removeKeysThatDontExists(array $customConfig, array $default): array { - $diff = array_diff_key($customConfig, $default); - foreach (array_keys($diff) as $invalidKey) { - unset($customConfig[$invalidKey]); - } - return $customConfig; - } - - public function validateToRenew(?IUser $user = null): void { - $this->throwIfMaximumValidityExpired(); - $this->throwIfAlreadySigned(); - $this->throwIfFileNotFound(); - } - public function save(): void { - $this->refreshIdFromDatabaseIfNecessary(); - if ($this->getEntity()->getId()) { - $this->identifyMethodMapper->update($this->getEntity()); - $this->notify(false); - } else { - $this->identifyMethodMapper->insertOrUpdate($this->getEntity()); - $this->notify(true); - } + $isNew = $this->identifyMethodService->save($this->getEntity()); + $this->notify($isNew); } public function delete(): void { - if ($this->getEntity()->getId()) { - $this->identifyMethodMapper->delete($this->getEntity()); - } + $this->identifyMethodService->delete($this->getEntity()); } - private function refreshIdFromDatabaseIfNecessary(): void { - $entity = $this->getEntity(); - if ($entity->getId()) { - return; - } - if (!$entity->getSignRequestId() || !$entity->getIdentifierKey()) { - return; + private function removeKeysThatDontExists(array $default): void { + $diff = array_diff_key($this->settings, $default); + foreach (array_keys($diff) as $invalidKey) { + unset($this->settings[$invalidKey]); } + } - $identifyMethods = $this->identifyMethodMapper->getIdentifyMethodsFromSignRequestId($entity->getSignRequestId()); - $exists = array_filter($identifyMethods, function (IdentifyMethod $current) use ($entity): bool { - return $current->getIdentifierKey() === $entity->getIdentifierKey(); - }); - if (!$exists) { - return; - } - $exists = current($exists); - $entity->setId($exists->getId()); + public function validateToRenew(?IUser $user = null): void { + $this->throwIfMaximumValidityExpired(); + $this->throwIfAlreadySigned(); + $this->throwIfFileNotFound(); } } diff --git a/lib/Service/IdentifyMethod/Account.php b/lib/Service/IdentifyMethod/Account.php index 41d4f0dcd9..90dcda0c99 100644 --- a/lib/Service/IdentifyMethod/Account.php +++ b/lib/Service/IdentifyMethod/Account.php @@ -24,20 +24,18 @@ namespace OCA\Libresign\Service\IdentifyMethod; -use OCA\Libresign\Db\FileMapper; use OCA\Libresign\Db\IdentifyMethodMapper; -use OCA\Libresign\Db\SignRequestMapper; use OCA\Libresign\Events\SendSignNotificationEvent; use OCA\Libresign\Exception\LibresignException; use OCA\Libresign\Helper\JSActions; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\ClickToSign; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\EmailToken; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\Password; use OCA\Libresign\Service\MailService; use OCA\Libresign\Service\SessionService; -use OCP\AppFramework\Services\IAppConfig; use OCP\AppFramework\Utility\ITimeFactory; use OCP\EventDispatcher\IEventDispatcher; -use OCP\Files\Config\IUserMountCache; use OCP\Files\IRootFolder; -use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; @@ -47,39 +45,31 @@ class Account extends AbstractIdentifyMethod { public function __construct( - private IAppConfig $appConfig, - private IL10N $l10n, + protected IdentifyMethodService $identifyMethodService, private IUserManager $userManager, - private SignRequestMapper $signRequestMapper, private IEventDispatcher $eventDispatcher, private IdentifyMethodMapper $identifyMethodMapper, - private FileMapper $fileMapper, private IUserSession $userSession, private IURLGenerator $urlGenerator, private IRootFolder $root, private IHasher $hasher, - private IUserMountCache $userMountCache, private ITimeFactory $timeFactory, private LoggerInterface $logger, private SessionService $sessionService, - private MailService $mail + private MailService $mail, + private Password $password, + private ClickToSign $clickToSign, + private EmailToken $emailToken, ) { // TRANSLATORS Name of possible authenticator method. This signalize that the signer could be identified by Nextcloud acccount - $this->friendlyName = $this->l10n->t('Account'); + $this->friendlyName = $this->identifyMethodService->getL10n()->t('Account'); + $this->signatureMethods = [ + $this->password->getName() => $this->password, + $this->clickToSign->getName() => $this->clickToSign, + $this->emailToken->getName() => $this->emailToken, + ]; parent::__construct( - $appConfig, - $l10n, - $identifyMethodMapper, - $signRequestMapper, - $fileMapper, - $root, - $hasher, - $userManager, - $urlGenerator, - $userMountCache, - $timeFactory, - $logger, - $sessionService, + $identifyMethodService, ); $this->getSettings(); } @@ -88,7 +78,7 @@ public function notify(bool $isNew): void { if (!$this->willNotify) { return; } - $signRequest = $this->signRequestMapper->getById($this->getEntity()->getSignRequestId()); + $signRequest = $this->identifyMethodService->getSignRequestMapper()->getById($this->getEntity()->getSignRequestId()); $this->eventDispatcher->dispatchTyped(new SendSignNotificationEvent( $signRequest, $this, @@ -99,7 +89,7 @@ public function notify(bool $isNew): void { public function validateToRequest(): void { $signer = $this->userManager->get($this->entity->getIdentifierValue()); if (!$signer) { - throw new LibresignException($this->l10n->t('User not found.')); + throw new LibresignException($this->identifyMethodService->getL10n()->t('User not found.')); } } @@ -123,7 +113,7 @@ private function getSigner(): IUser { if (empty($signer) || count($signer) > 1) { throw new LibresignException(json_encode([ 'action' => JSActions::ACTION_DO_NOTHING, - 'errors' => [$this->l10n->t('Invalid user')], + 'errors' => [$this->identifyMethodService->getL10n()->t('Invalid user')], ])); } $signer = current($signer); @@ -135,17 +125,17 @@ private function authenticatedUserIsTheSigner(IUser $user, IUser $signer): void if ($user !== $signer) { throw new LibresignException(json_encode([ 'action' => JSActions::ACTION_DO_NOTHING, - 'errors' => [$this->l10n->t('Invalid user')], + 'errors' => [$this->identifyMethodService->getL10n()->t('Invalid user')], ])); } } private function throwIfNotAuthenticated(?IUser $user = null): void { if (!$user instanceof IUser) { - $signRequest = $this->signRequestMapper->getById($this->getEntity()->getSignRequestId()); + $signRequest = $this->identifyMethodService->getSignRequestMapper()->getById($this->getEntity()->getSignRequestId()); throw new LibresignException(json_encode([ 'action' => JSActions::ACTION_REDIRECT, - 'errors' => [$this->l10n->t('You are not logged in. Please log in.')], + 'errors' => [$this->identifyMethodService->getL10n()->t('You are not logged in. Please log in.')], 'redirect' => $this->urlGenerator->linkToRoute('core.login.showLoginForm', [ 'redirect_url' => $this->urlGenerator->linkToRoute( 'libresign.page.sign', @@ -169,7 +159,7 @@ public function getSettings(): array { } private function isEnabledByDefault(): bool { - $config = $this->appConfig->getAppValue('identify_methods', '[]'); + $config = $this->identifyMethodService->getAppConfig()->getAppValue('identify_methods', '[]'); $config = json_decode($config, true); if (json_last_error() !== JSON_ERROR_NONE || !is_array($config)) { return true; diff --git a/lib/Service/IdentifyMethod/Email.php b/lib/Service/IdentifyMethod/Email.php index 5258f66573..35ac971152 100644 --- a/lib/Service/IdentifyMethod/Email.php +++ b/lib/Service/IdentifyMethod/Email.php @@ -25,58 +25,37 @@ namespace OCA\Libresign\Service\IdentifyMethod; use OCA\Libresign\Db\FileElementMapper; -use OCA\Libresign\Db\FileMapper; use OCA\Libresign\Db\IdentifyMethodMapper; -use OCA\Libresign\Db\SignRequestMapper; use OCA\Libresign\Exception\LibresignException; use OCA\Libresign\Helper\JSActions; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\ClickToSign; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\EmailToken; use OCA\Libresign\Service\MailService; use OCA\Libresign\Service\SessionService; -use OCP\AppFramework\Services\IAppConfig; use OCP\AppFramework\Utility\ITimeFactory; -use OCP\Files\Config\IUserMountCache; use OCP\Files\IRootFolder; -use OCP\IL10N; -use OCP\IURLGenerator; use OCP\IUser; -use OCP\IUserManager; -use OCP\Security\IHasher; -use Psr\Log\LoggerInterface; class Email extends AbstractIdentifyMethod { public function __construct( - private IAppConfig $appConfig, - private IL10N $l10n, + protected IdentifyMethodService $identifyMethodService, private MailService $mail, - private SignRequestMapper $signRequestMapper, private IdentifyMethodMapper $identifyMethodMapper, - private FileMapper $fileMapper, - private IUserManager $userManager, - private IURLGenerator $urlGenerator, private IRootFolder $root, - private IHasher $hasher, - private IUserMountCache $userMountCache, private ITimeFactory $timeFactory, - private LoggerInterface $logger, private SessionService $sessionService, private FileElementMapper $fileElementMapper, + private ClickToSign $clickToSign, + private EmailToken $emailToken, ) { // TRANSLATORS Name of possible authenticator method. This signalize that the signer could be identified by email - $this->friendlyName = $this->l10n->t('Email'); + $this->friendlyName = $this->identifyMethodService->getL10n()->t('Email'); + $this->signatureMethods = [ + $this->clickToSign->getName() => $this->clickToSign, + $this->emailToken->getName() => $this->emailToken, + ]; parent::__construct( - $appConfig, - $l10n, - $identifyMethodMapper, - $signRequestMapper, - $fileMapper, - $root, - $hasher, - $userManager, - $urlGenerator, - $userMountCache, - $timeFactory, - $logger, - $sessionService, + $identifyMethodService, ); $this->getSettings(); } @@ -85,7 +64,7 @@ public function notify(bool $isNew): void { if (!$this->willNotify) { return; } - $signRequest = $this->signRequestMapper->getById($this->getEntity()->getSignRequestId()); + $signRequest = $this->identifyMethodService->getSignRequestMapper()->getById($this->getEntity()->getSignRequestId()); if ($isNew) { $this->mail->notifyUnsignedUser($signRequest, $this->getEntity()->getIdentifierValue()); return; @@ -121,7 +100,7 @@ private function throwIfIsAuthenticatedWithDifferentAccount(?IUser $user): void } throw new LibresignException(json_encode([ 'action' => JSActions::ACTION_DO_NOTHING, - 'errors' => [$this->l10n->t('Invalid user')], + 'errors' => [$this->identifyMethodService->getL10n()->t('Invalid user')], ])); } } @@ -131,7 +110,7 @@ private function throwIfAccountAlreadyExists(?IUser $user): void { return; } $email = $this->entity->getIdentifierValue(); - $signer = $this->userManager->getByEmail($email); + $signer = $this->identifyMethodService->getUserManager()->getByEmail($email); if (!$signer) { return; } @@ -140,12 +119,12 @@ private function throwIfAccountAlreadyExists(?IUser $user): void { return; } } - $signRequest = $this->signRequestMapper->getById($this->getEntity()->getSignRequestId()); + $signRequest = $this->identifyMethodService->getSignRequestMapper()->getById($this->getEntity()->getSignRequestId()); throw new LibresignException(json_encode([ 'action' => JSActions::ACTION_REDIRECT, - 'errors' => [$this->l10n->t('User already exists. Please login.')], - 'redirect' => $this->urlGenerator->linkToRoute('core.login.showLoginForm', [ - 'redirect_url' => $this->urlGenerator->linkToRoute( + 'errors' => [$this->identifyMethodService->getL10n()->t('User already exists. Please login.')], + 'redirect' => $this->identifyMethodService->getUrlGenerator()->linkToRoute('core.login.showLoginForm', [ + 'redirect_url' => $this->identifyMethodService->getUrlGenerator()->linkToRoute( 'libresign.page.sign', ['uuid' => $signRequest->getUuid()] ), @@ -156,11 +135,11 @@ private function throwIfAccountAlreadyExists(?IUser $user): void { public function validateToCreateAccount(string $value): void { $this->throwIfInvalidEmail(); $this->throwIfNotAllowedToCreateAccount(); - if ($this->userManager->userExists($value)) { - throw new LibresignException($this->l10n->t('User already exists')); + if ($this->identifyMethodService->getUserManager()->userExists($value)) { + throw new LibresignException($this->identifyMethodService->getL10n()->t('User already exists')); } if ($this->getEntity()->getIdentifierValue() !== $value) { - throw new LibresignException($this->l10n->t('This is not your file')); + throw new LibresignException($this->identifyMethodService->getL10n()->t('This is not your file')); } } @@ -168,14 +147,14 @@ private function throwIfNotAllowedToCreateAccount(): void { if (!$this->canCreateAccount) { throw new LibresignException(json_encode([ 'action' => JSActions::ACTION_SHOW_ERROR, - 'errors' => [$this->l10n->t('It is not possible to create new accounts.')], + 'errors' => [$this->identifyMethodService->getL10n()->t('It is not possible to create new accounts.')], ])); } } private function throwIfInvalidEmail(): void { if (!filter_var($this->entity->getIdentifierValue(), FILTER_VALIDATE_EMAIL)) { - throw new LibresignException($this->l10n->t('Invalid email')); + throw new LibresignException($this->identifyMethodService->getL10n()->t('Invalid email')); } } @@ -189,7 +168,7 @@ public function getSettings(): array { 'can_create_account' => $this->canCreateAccount, ], immutable: [ - 'test_url' => $this->urlGenerator->linkToRoute('settings.MailSettings.sendTestMail'), + 'test_url' => $this->identifyMethodService->getUrlGenerator()->linkToRoute('settings.MailSettings.sendTestMail'), ] ); $this->canCreateAccount = $this->settings['can_create_account']; diff --git a/lib/Service/IdentifyMethod/IIdentifyMethod.php b/lib/Service/IdentifyMethod/IIdentifyMethod.php index ffe2be870e..032af6b28f 100644 --- a/lib/Service/IdentifyMethod/IIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/IIdentifyMethod.php @@ -35,6 +35,7 @@ public function setUser(?IUser $user): void; public function cleanEntity(): void; public function setEntity(IdentifyMethod $entity): void; public function getEntity(): IdentifyMethod; + public function getSignatureMethods(): array; public function getSettings(): array; public function willNotifyUser(bool $willNotify): void; public function notify(bool $isNew): void; diff --git a/lib/Service/IdentifyMethod/IdentifyMethodService.php b/lib/Service/IdentifyMethod/IdentifyMethodService.php new file mode 100644 index 0000000000..76e8196df5 --- /dev/null +++ b/lib/Service/IdentifyMethod/IdentifyMethodService.php @@ -0,0 +1,145 @@ + + * + * @author Vitor Mattos + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCA\Libresign\Service\IdentifyMethod; + +use OCA\Libresign\Db\FileMapper; +use OCA\Libresign\Db\IdentifyMethod; +use OCA\Libresign\Db\IdentifyMethodMapper; +use OCA\Libresign\Db\SignRequestMapper; +use OCA\Libresign\Service\SessionService; +use OCP\AppFramework\Services\IAppConfig; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\Config\IUserMountCache; +use OCP\Files\IRootFolder; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\IUserManager; +use OCP\Security\IHasher; +use Psr\Log\LoggerInterface; + +class IdentifyMethodService { + public function __construct( + private IdentifyMethodMapper $identifyMethodMapper, + private SessionService $sessionService, + private ITimeFactory $timeFactory, + private IRootFolder $root, + private IAppConfig $appConfig, + private SignRequestMapper $signRequestMapper, + private IUserMountCache $userMountCache, + private IL10N $l10n, + private FileMapper $fileMapper, + private IHasher $hasher, + private IUserManager $userManager, + private IURLGenerator $urlGenerator, + private LoggerInterface $logger, + ) { + } + + /** + * @return boolean is new instance + */ + public function save(IdentifyMethod $identifyMethod): bool { + $this->refreshIdFromDatabaseIfNecessary($identifyMethod); + if ($identifyMethod->getId()) { + $this->identifyMethodMapper->update($identifyMethod); + return false; + } + $this->identifyMethodMapper->insertOrUpdate($identifyMethod); + return true; + } + + public function delete(IdentifyMethod $identifyMethod): void { + if ($identifyMethod->getId()) { + $this->identifyMethodMapper->delete($identifyMethod); + } + } + + private function refreshIdFromDatabaseIfNecessary(IdentifyMethod $identifyMethod): void { + if ($identifyMethod->getId()) { + return; + } + if (!$identifyMethod->getSignRequestId() || !$identifyMethod->getIdentifierKey()) { + return; + } + + $identifyMethods = $this->identifyMethodMapper->getIdentifyMethodsFromSignRequestId($identifyMethod->getSignRequestId()); + $exists = array_filter($identifyMethods, function (IdentifyMethod $current) use ($identifyMethod): bool { + return $current->getIdentifierKey() === $identifyMethod->getIdentifierKey(); + }); + if (!$exists) { + return; + } + $exists = current($exists); + $identifyMethod->setId($exists->getId()); + } + + public function getSessionService(): SessionService { + return $this->sessionService; + } + + public function getTimeFactory(): ITimeFactory { + return $this->timeFactory; + } + + public function getRootFolder(): IRootFolder { + return $this->root; + } + + public function getAppConfig(): IAppConfig { + return $this->appConfig; + } + + public function getSignRequestMapper(): SignRequestMapper { + return $this->signRequestMapper; + } + + public function getUserMountCache(): IUserMountCache { + return $this->userMountCache; + } + + public function getL10n(): IL10N { + return $this->l10n; + } + + public function getFileMapper(): FileMapper { + return $this->fileMapper; + } + + public function getHasher(): IHasher { + return $this->hasher; + } + + public function getUserManager(): IUserManager { + return $this->userManager; + } + + public function getUrlGenerator(): IURLGenerator { + return $this->urlGenerator; + } + + public function getLogger(): LoggerInterface { + return $this->logger; + } +} diff --git a/lib/Service/IdentifyMethod/SignatureMethod/ClickToSign.php b/lib/Service/IdentifyMethod/SignatureMethod/ClickToSign.php index 5cedbb949b..65fec18e2f 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/ClickToSign.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/ClickToSign.php @@ -24,56 +24,16 @@ namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; -use OCA\Libresign\Db\FileMapper; -use OCA\Libresign\Db\IdentifyMethodMapper; -use OCA\Libresign\Db\SignRequestMapper; -use OCA\Libresign\Handler\Pkcs12Handler; -use OCA\Libresign\Service\MailService; -use OCA\Libresign\Service\SessionService; -use OCP\AppFramework\Services\IAppConfig; -use OCP\AppFramework\Utility\ITimeFactory; -use OCP\Files\Config\IUserMountCache; -use OCP\Files\IRootFolder; -use OCP\IL10N; -use OCP\IURLGenerator; -use OCP\IUserManager; -use OCP\Security\IHasher; -use Psr\Log\LoggerInterface; +use OCA\Libresign\Service\IdentifyMethod\IdentifyMethodService; class ClickToSign extends AbstractSignatureMethod { public function __construct( - private IAppConfig $appConfig, - private IL10N $l10n, - private MailService $mail, - private SignRequestMapper $signRequestMapper, - private IdentifyMethodMapper $identifyMethodMapper, - private FileMapper $fileMapper, - private IUserManager $userManager, - private IURLGenerator $urlGenerator, - private IRootFolder $root, - private IHasher $hasher, - private IUserMountCache $userMountCache, - private ITimeFactory $timeFactory, - private LoggerInterface $logger, - private SessionService $sessionService, - private Pkcs12Handler $pkcs12Handler, + protected IdentifyMethodService $identifyMethodService, ) { // TRANSLATORS Name of possible authenticator method. This signalize that the signer only need to click to sign after was identified - $this->friendlyName = $this->l10n->t('Click to sign'); + $this->friendlyName = $this->identifyMethodService->getL10n()->t('Click to sign'); parent::__construct( - $appConfig, - $l10n, - $identifyMethodMapper, - $signRequestMapper, - $fileMapper, - $root, - $hasher, - $userManager, - $urlGenerator, - $userMountCache, - $timeFactory, - $logger, - $sessionService, + $identifyMethodService, ); } } diff --git a/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php index 318d960b6d..8f5fb83b76 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php @@ -24,73 +24,17 @@ namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; -use OCA\Libresign\Db\FileElementMapper; -use OCA\Libresign\Db\FileMapper; -use OCA\Libresign\Db\IdentifyMethodMapper; -use OCA\Libresign\Db\SignRequestMapper; -use OCA\Libresign\Service\MailService; -use OCA\Libresign\Service\SessionService; -use OCP\AppFramework\Utility\ITimeFactory; -use OCP\Files\Config\IUserMountCache; -use OCP\Files\IRootFolder; -use OCP\IConfig; -use OCP\IL10N; -use OCP\IURLGenerator; -use OCP\IUserManager; -use OCP\Security\IHasher; -use Psr\Log\LoggerInterface; +use OCA\Libresign\Service\IdentifyMethod\IdentifyMethodService; class EmailToken extends AbstractSignatureMethod { public const ID = 'email'; public function __construct( - private IConfig $config, - private IL10N $l10n, - private MailService $mail, - private SignRequestMapper $signRequestMapper, - private IdentifyMethodMapper $identifyMethodMapper, - private FileMapper $fileMapper, - private IUserManager $userManager, - private IURLGenerator $urlGenerator, - private IRootFolder $root, - private IHasher $hasher, - private IUserMountCache $userMountCache, - private ITimeFactory $timeFactory, - private LoggerInterface $logger, - private SessionService $sessionService, - private FileElementMapper $fileElementMapper, + protected IdentifyMethodService $identifyMethodService, ) { // TRANSLATORS Name of possible authenticator method. This signalize that the signer could be identified by email - $this->friendlyName = $this->l10n->t('Email token'); + $this->friendlyName = $this->identifyMethodService->getL10n()->t('Email token'); parent::__construct( - $config, - $l10n, - $identifyMethodMapper, - $signRequestMapper, - $fileMapper, - $root, - $hasher, - $userManager, - $urlGenerator, - $userMountCache, - $timeFactory, - $logger, - $sessionService, + $identifyMethodService, ); - $this->getSettings(); - } - - public function getSettings(): array { - if (!empty($this->settings)) { - return $this->settings; - } - $this->settings = parent::getSettingsFromDatabase( - default: [ - 'enabled' => false, - ], - immutable: [ - 'test_url' => $this->urlGenerator->linkToRoute('settings.MailSettings.sendTestMail'), - ] - ); - return $this->settings; } } diff --git a/lib/Service/IdentifyMethod/SignatureMethod/Password.php b/lib/Service/IdentifyMethod/SignatureMethod/Password.php index 2443e43747..307b80aab0 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/Password.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/Password.php @@ -24,58 +24,20 @@ namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; -use OCA\Libresign\Db\FileMapper; -use OCA\Libresign\Db\IdentifyMethodMapper; -use OCA\Libresign\Db\SignRequestMapper; use OCA\Libresign\Exception\LibresignException; use OCA\Libresign\Handler\Pkcs12Handler; -use OCA\Libresign\Service\MailService; -use OCA\Libresign\Service\SessionService; -use OCP\AppFramework\Services\IAppConfig; -use OCP\AppFramework\Utility\ITimeFactory; -use OCP\Files\Config\IUserMountCache; -use OCP\Files\IRootFolder; -use OCP\IL10N; -use OCP\IURLGenerator; -use OCP\IUserManager; -use OCP\Security\IHasher; -use Psr\Log\LoggerInterface; +use OCA\Libresign\Service\IdentifyMethod\IdentifyMethodService; class Password extends AbstractSignatureMethod { public const ID = 'password'; public function __construct( - private IAppConfig $appConfig, - private IL10N $l10n, - private MailService $mail, - private SignRequestMapper $signRequestMapper, - private IdentifyMethodMapper $identifyMethodMapper, - private FileMapper $fileMapper, - private IUserManager $userManager, - private IURLGenerator $urlGenerator, - private IRootFolder $root, - private IHasher $hasher, - private IUserMountCache $userMountCache, - private ITimeFactory $timeFactory, - private LoggerInterface $logger, - private SessionService $sessionService, - private Pkcs12Handler $pkcs12Handler, + protected IdentifyMethodService $identifyMethodService, + protected Pkcs12Handler $pkcs12Handler, ) { // TRANSLATORS Name of possible authenticator method. This signalize that the signer could be identified by certificate password - $this->friendlyName = $this->l10n->t('Certificate with password'); + $this->friendlyName = $this->identifyMethodService->getL10n()->t('Certificate with password'); parent::__construct( - $appConfig, - $l10n, - $identifyMethodMapper, - $signRequestMapper, - $fileMapper, - $root, - $hasher, - $userManager, - $urlGenerator, - $userMountCache, - $timeFactory, - $logger, - $sessionService, + $identifyMethodService, ); } @@ -83,37 +45,7 @@ public function validateToIdentify(): void { $pfx = $this->pkcs12Handler->getPfx($this->user->getUID()); openssl_pkcs12_read($pfx, $cert_info, $this->getEntity()->getIdentifierValue()); if (empty($cert_info)) { - throw new LibresignException($this->l10n->t('Invalid password')); + throw new LibresignException($this->identifyMethodService->getL10n()->t('Invalid password')); } } - - public function getSettings(): array { - if (!empty($this->settings)) { - return $this->settings; - } - - if (!$this->sessionService->isAuthenticated()) { - $isEnabledAsSignatueMethod = false; - } else { - $config = $this->appConfig->getAppValue('signature_methods', '[]'); - $config = json_decode($config, true); - if (json_last_error() !== JSON_ERROR_NONE || !is_array($config)) { - $isEnabledAsSignatueMethod = true; - } else { - $isEnabledAsSignatueMethod = array_reduce($config, function (bool $carry, $method) { - if (!is_array($method)) { - $carry = false; - } elseif (array_key_exists('enabled', $method)) { - $carry = ((bool) $method['enabled']) || !$carry; - } - return $carry; - }, true); - } - } - - $this->settings = $this->getSettingsFromDatabase(); - $this->settings['enabled_as_signature_method'] = $isEnabledAsSignatueMethod; - - return $this->settings; - } } From 6e482338ddf20d47fc99874cb34a11d45b355bd7 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 2 Feb 2024 18:46:23 -0300 Subject: [PATCH 10/42] Remove unecessary condition Signed-off-by: Vitor Mattos --- src/views/Settings/IdentifierFactor.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/Settings/IdentifierFactor.vue b/src/views/Settings/IdentifierFactor.vue index 754bc1e3b0..fdc787c312 100644 --- a/src/views/Settings/IdentifierFactor.vue +++ b/src/views/Settings/IdentifierFactor.vue @@ -9,7 +9,7 @@ {{ option.friendly_name }}
-
+
{{ t('libresign', 'Request to create account when the user does not have an account') }} From f649cd8184cf23897643184f9e2be0bda7d1695e Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 2 Feb 2024 18:51:13 -0300 Subject: [PATCH 11/42] Fix the event that occurs when change the settiongs Signed-off-by: Vitor Mattos --- src/views/Settings/IdentifierFactor.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/Settings/IdentifierFactor.vue b/src/views/Settings/IdentifierFactor.vue index fdc787c312..5484e9da70 100644 --- a/src/views/Settings/IdentifierFactor.vue +++ b/src/views/Settings/IdentifierFactor.vue @@ -11,13 +11,13 @@
+ @update:checked="save()"> {{ t('libresign', 'Request to create account when the user does not have an account') }}
+ @update:checked="save()"> {{ t('libresign', 'Make this method required') }}
From 3b9a9b0f8e29fd7e484993b3a43855151a8fcaaa Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 2 Feb 2024 18:52:05 -0300 Subject: [PATCH 12/42] Change de logic to remove the label Have other settings that we want to maintain Signed-off-by: Vitor Mattos --- src/views/Settings/IdentifierFactor.vue | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/views/Settings/IdentifierFactor.vue b/src/views/Settings/IdentifierFactor.vue index 5484e9da70..aae9659784 100644 --- a/src/views/Settings/IdentifierFactor.vue +++ b/src/views/Settings/IdentifierFactor.vue @@ -92,13 +92,15 @@ export default { this.flagFirstSignatureMethodIfAllDisabled() // Get only enabled let props = this.options.filter(item => item.enabled) - // Save only enabled property, removing other properties + // Remove label from signature method, we don't need to save this props = JSON.parse(JSON.stringify(props)) .map(item => { Object.keys(item.signatureMethods).forEach(id => { - item.signatureMethods[id] = { - enabled: item.signatureMethods[id].enabled, - } + Object.keys(item.signatureMethods[id]).forEach(signatureMethdoPropName => { + if (signatureMethdoPropName === 'label') { + delete item.signatureMethods[id][signatureMethdoPropName] + } + }) }) return item }) From e6480e510d80cecb1cef6e8da2dfdecf06e77aab Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 2 Feb 2024 19:52:25 -0300 Subject: [PATCH 13/42] Remove no more used initial state Signed-off-by: Vitor Mattos --- lib/Settings/Admin.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index f4d7086bf3..fbef8826af 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -49,10 +49,6 @@ public function getForm(): TemplateResponse { 'identify_methods', $this->identifyMethodService->getIdentifyMethodsSettings() ); - $this->initialState->provideInitialState( - 'signature_methods', - $this->SignatureMethodService->getMethods() - ); $this->initialState->provideInitialState( 'certificate_engine', $this->certificateEngineHandler->getEngine()->getName() From 21bf5280372cbce65e1a576fe5ed21ae4b47bd0c Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 2 Feb 2024 19:53:18 -0300 Subject: [PATCH 14/42] Return all sign methods of identified factors Signed-off-by: Vitor Mattos --- lib/Controller/PageController.php | 4 ++-- .../IdentifyMethod/AbstractIdentifyMethod.php | 6 +++++- lib/Service/IdentifyMethod/IIdentifyMethod.php | 1 + lib/Service/IdentifyMethodService.php | 16 ++++++++++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index f718e9c0d4..7081e8f5e7 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -165,7 +165,7 @@ public function sign($uuid): TemplateResponse { $this->initialState->provideInitialState('visibleElements', $file['visibleElements']); $this->initialState->provideInitialState('signers', $file['signers']); $this->provideSignerSignatues(); - $signatureMethods = $this->signatureMethodService->getMethods(); + $signatureMethods = $this->identifyMethodService->getSignMethodsOfIdentifiedFactors($this->getSignRequestEntity()->getId()); $this->provideBlurredEmail($signatureMethods, $this->userSession->getUser()?->getEMailAddress()); $this->initialState->provideInitialState('signature_methods', $signatureMethods); $this->initialState->provideInitialState('token_length', SignatureMethodService::TOKEN_LENGTH); @@ -255,7 +255,7 @@ public function signAccountFile($uuid): TemplateResponse { $this->initialState->provideInitialState('visibleElements', []); $this->initialState->provideInitialState('signers', []); $this->provideSignerSignatues(); - $signatureMethods = $this->signatureMethodService->getMethods(); + $signatureMethods = $this->identifyMethodService->getSignMethodsOfIdentifiedFactors($this->getSignRequestEntity()->getId()); $this->provideBlurredEmail($signatureMethods, $this->userSession->getUser()?->getEMailAddress()); $this->initialState->provideInitialState('signature_methods', $signatureMethods); $this->initialState->provideInitialState('token_length', SignatureMethodService::TOKEN_LENGTH); diff --git a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php index 1498e92b92..00483c0b61 100644 --- a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php @@ -38,7 +38,7 @@ abstract class AbstractIdentifyMethod implements IIdentifyMethod { protected bool $canCreateAccount = true; protected IdentifyMethod $entity; protected string $name; - public string $friendlyName; + protected string $friendlyName; protected ?IUser $user = null; protected string $codeSentByUser = ''; protected array $settings = []; @@ -59,6 +59,10 @@ public function getName(): string { return $this->name; } + public function getFriendlyName(): string { + return $this->friendlyName; + } + public function isEnabledAsSignatueMethod(): bool { $settings = $this->getSettings(); return $settings['enabled_as_signature_method']; diff --git a/lib/Service/IdentifyMethod/IIdentifyMethod.php b/lib/Service/IdentifyMethod/IIdentifyMethod.php index 032af6b28f..90d6c18ecb 100644 --- a/lib/Service/IdentifyMethod/IIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/IIdentifyMethod.php @@ -29,6 +29,7 @@ interface IIdentifyMethod { public function getName(): string; + public function getFriendlyName(): string; public function isEnabledAsSignatueMethod(): bool; public function setCodeSentByUser(string $code): void; public function setUser(?IUser $user): void; diff --git a/lib/Service/IdentifyMethodService.php b/lib/Service/IdentifyMethodService.php index 5353db5a48..f9e8eb0f7f 100644 --- a/lib/Service/IdentifyMethodService.php +++ b/lib/Service/IdentifyMethodService.php @@ -158,6 +158,22 @@ public function getIdentifyMethodsFromSignRequestId(int $signRequestId): array { return $return; } + public function getSignMethodsOfIdentifiedFactors(int $signRequestId): array { + $matrix = $this->getIdentifyMethodsFromSignRequestId($signRequestId); + $return = []; + foreach ($matrix as $methods) { + foreach ($methods as $method) { + if (empty($method->getEntity()->getIdentifiedAtDate())) { + continue; + } + $return[$method->getName()] = [ + 'label' => $method->getFriendlyName(), + ]; + } + } + return $return; + } + public function save(SignRequest $signRequest, bool $notify = true): void { foreach ($this->identifyMethods as $methods) { foreach ($methods as $identifyMethod) { From 6cacf8197ef63b56a6a288e95e2ad085d16c8c5e Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 2 Feb 2024 19:54:06 -0300 Subject: [PATCH 15/42] Lint fix Signed-off-by: Vitor Mattos --- src/Components/File/AppFilesTab.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/File/AppFilesTab.vue b/src/Components/File/AppFilesTab.vue index 44d8fb2ecb..60dcd7fce8 100644 --- a/src/Components/File/AppFilesTab.vue +++ b/src/Components/File/AppFilesTab.vue @@ -48,7 +48,7 @@ export default { this.signers = [] this.file = { nodeId: fileInfo.id, - name: fileInfo.name + name: fileInfo.name, } this.requestedBy = {} this.requestDate = '' From 072a9e3b396943686a76980452034b10e0bd07cb Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 2 Feb 2024 20:08:46 -0300 Subject: [PATCH 16/42] Improve check if property exists Signed-off-by: Vitor Mattos --- src/views/Settings/CertificateCustonOptions.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/views/Settings/CertificateCustonOptions.vue b/src/views/Settings/CertificateCustonOptions.vue index 0a54404dbf..8c9589589b 100644 --- a/src/views/Settings/CertificateCustonOptions.vue +++ b/src/views/Settings/CertificateCustonOptions.vue @@ -87,8 +87,7 @@ export default { return item.value.length >= item.min }, validateMax(item) { - // eslint-disable-next-line no-prototype-builtins - if (item.hasOwnProperty('max')) { + if (Object.hasOwn(item, 'max')) { return item.value.length <= item.max } return true From cc94f38ecc68effb81c4dbacc0b36e3d28b5dced Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 2 Feb 2024 20:09:11 -0300 Subject: [PATCH 17/42] Prevent error when try to access property that don't exists Signed-off-by: Vitor Mattos --- src/views/SignPDF/_partials/Sign.vue | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/views/SignPDF/_partials/Sign.vue b/src/views/SignPDF/_partials/Sign.vue index 759b8d889b..fde984906c 100644 --- a/src/views/SignPDF/_partials/Sign.vue +++ b/src/views/SignPDF/_partials/Sign.vue @@ -43,7 +43,7 @@

-
- - - Date: Fri, 2 Feb 2024 21:44:28 -0300 Subject: [PATCH 19/42] Check if signature method is enabled Signed-off-by: Vitor Mattos --- .../IdentifyMethod/AbstractIdentifyMethod.php | 27 ++++++++--------- .../IdentifyMethod/IIdentifyMethod.php | 2 +- .../IdentifyMethod/IdentifyMethodService.php | 13 ++++++++ .../AbstractSignatureMethod.php | 11 ++++++- .../SignatureMethod/ISignatureMethod.php | 30 +++++++++++++++++++ lib/Service/IdentifyMethodService.php | 17 +++++++---- 6 files changed, 78 insertions(+), 22 deletions(-) create mode 100644 lib/Service/IdentifyMethod/SignatureMethod/ISignatureMethod.php diff --git a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php index 00483c0b61..79862ee602 100644 --- a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php @@ -63,11 +63,6 @@ public function getFriendlyName(): string { return $this->friendlyName; } - public function isEnabledAsSignatueMethod(): bool { - $settings = $this->getSettings(); - return $settings['enabled_as_signature_method']; - } - public function setCodeSentByUser(string $code): void { $this->codeSentByUser = $code; } @@ -89,15 +84,19 @@ public function getEntity(): IdentifyMethod { return $this->entity; } - public function getSignatureMethods(): array { + public function signatureMethodsToArray(): array { return array_map(function (AbstractSignatureMethod $method) { return [ 'label' => $method->friendlyName, - 'enabled' => $method->isEnabledAsSignatueMethod(), + 'enabled' => $method->isEnabled(), ]; }, $this->signatureMethods); } + public function getSignatureMethods(): array { + return $this->signatureMethods; + } + public function getSettings(): array { $this->getSettingsFromDatabase(); return $this->settings; @@ -272,7 +271,7 @@ protected function getSettingsFromDatabase(array $default = [], array $immutable 'friendly_name' => $this->friendlyName, 'enabled' => true, 'mandatory' => true, - 'signatureMethods' => $this->getSignatureMethods(), + 'signatureMethods' => $this->signatureMethodsToArray(), ], $default ); @@ -287,18 +286,18 @@ private function overrideImmutable(array $immutable): void { } private function loadSavedSettings(): void { - $config = $this->identifyMethodService->getAppConfig()->getAppValue('identify_methods', '[]'); - $config = json_decode($config, true); - if (json_last_error() !== JSON_ERROR_NONE || !is_array($config)) { - $this->settings = []; - return; - } + $config = $this->identifyMethodService->getSavedSettings(); $this->settings = array_reduce($config, function ($carry, $config) { if ($config['name'] === $this->name) { return $config; } return $carry; }, []); + foreach ($this->settings['signatureMethods'] as $method => $settings) { + if (is_object($this->signatureMethods[$method]) && isset($settings['enabled']) && $settings['enabled']) { + $this->signatureMethods[$method]->enable(); + } + } } private function applyDefault(array $customConfig, array $default): array { diff --git a/lib/Service/IdentifyMethod/IIdentifyMethod.php b/lib/Service/IdentifyMethod/IIdentifyMethod.php index 90d6c18ecb..81f666c2d7 100644 --- a/lib/Service/IdentifyMethod/IIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/IIdentifyMethod.php @@ -30,13 +30,13 @@ interface IIdentifyMethod { public function getName(): string; public function getFriendlyName(): string; - public function isEnabledAsSignatueMethod(): bool; public function setCodeSentByUser(string $code): void; public function setUser(?IUser $user): void; public function cleanEntity(): void; public function setEntity(IdentifyMethod $entity): void; public function getEntity(): IdentifyMethod; public function getSignatureMethods(): array; + public function signatureMethodsToArray(): array; public function getSettings(): array; public function willNotifyUser(bool $willNotify): void; public function notify(bool $isNew): void; diff --git a/lib/Service/IdentifyMethod/IdentifyMethodService.php b/lib/Service/IdentifyMethod/IdentifyMethodService.php index 76e8196df5..56dfe95096 100644 --- a/lib/Service/IdentifyMethod/IdentifyMethodService.php +++ b/lib/Service/IdentifyMethod/IdentifyMethodService.php @@ -40,6 +40,7 @@ use Psr\Log\LoggerInterface; class IdentifyMethodService { + private array $savedSettings = []; public function __construct( private IdentifyMethodMapper $identifyMethodMapper, private SessionService $sessionService, @@ -95,6 +96,18 @@ private function refreshIdFromDatabaseIfNecessary(IdentifyMethod $identifyMethod $identifyMethod->setId($exists->getId()); } + public function getSavedSettings(): array { + if (!empty($this->savedSettings)) { + return $this->savedSettings; + } + $config = $this->getAppConfig()->getAppValue('identify_methods', '[]'); + $config = json_decode($config, true); + if (is_array($config)) { + $this->savedSettings = $config; + } + return $this->savedSettings; + } + public function getSessionService(): SessionService { return $this->sessionService; } diff --git a/lib/Service/IdentifyMethod/SignatureMethod/AbstractSignatureMethod.php b/lib/Service/IdentifyMethod/SignatureMethod/AbstractSignatureMethod.php index e03eeacb78..ed264965a5 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/AbstractSignatureMethod.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/AbstractSignatureMethod.php @@ -26,5 +26,14 @@ use OCA\Libresign\Service\IdentifyMethod\AbstractIdentifyMethod; -abstract class AbstractSignatureMethod extends AbstractIdentifyMethod { +abstract class AbstractSignatureMethod extends AbstractIdentifyMethod implements ISignatureMethod { + private bool $enabled = false; + + public function enable(): void { + $this->enabled = true; + } + + public function isEnabled(): bool { + return $this->enabled; + } } diff --git a/lib/Service/IdentifyMethod/SignatureMethod/ISignatureMethod.php b/lib/Service/IdentifyMethod/SignatureMethod/ISignatureMethod.php new file mode 100644 index 0000000000..715af4ea4c --- /dev/null +++ b/lib/Service/IdentifyMethod/SignatureMethod/ISignatureMethod.php @@ -0,0 +1,30 @@ + + * + * @author Vitor Mattos + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; + +interface ISignatureMethod { + public function enable(): void; + public function isEnabled(): bool; +} diff --git a/lib/Service/IdentifyMethodService.php b/lib/Service/IdentifyMethodService.php index f9e8eb0f7f..6f466f8391 100644 --- a/lib/Service/IdentifyMethodService.php +++ b/lib/Service/IdentifyMethodService.php @@ -161,14 +161,19 @@ public function getIdentifyMethodsFromSignRequestId(int $signRequestId): array { public function getSignMethodsOfIdentifiedFactors(int $signRequestId): array { $matrix = $this->getIdentifyMethodsFromSignRequestId($signRequestId); $return = []; - foreach ($matrix as $methods) { - foreach ($methods as $method) { - if (empty($method->getEntity()->getIdentifiedAtDate())) { + foreach ($matrix as $identifyMethods) { + foreach ($identifyMethods as $identifyMethod) { + if (empty($identifyMethod->getEntity()->getIdentifiedAtDate())) { continue; } - $return[$method->getName()] = [ - 'label' => $method->getFriendlyName(), - ]; + foreach ($identifyMethod->getSignatureMethods() as $signatureMethod) { + if (!$signatureMethod->isEnabled()) { + continue; + } + $return[$signatureMethod->getName()] = [ + 'label' => $signatureMethod->getFriendlyName(), + ]; + } } } return $return; From 7b767af78cdf77b0a77cb60f6c13efac2116421c Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 2 Feb 2024 21:47:58 -0300 Subject: [PATCH 20/42] Add file header Signed-off-by: Vitor Mattos --- tests/stubs/oc_hooks_emitter.php | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/stubs/oc_hooks_emitter.php b/tests/stubs/oc_hooks_emitter.php index ed72d9c3aa..58ab7fce1e 100644 --- a/tests/stubs/oc_hooks_emitter.php +++ b/tests/stubs/oc_hooks_emitter.php @@ -1,10 +1,32 @@ + * + * @author Vitor Mattos + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + namespace OC\Hooks { class Emitter { public function emit(string $class, string $value, array $option) { } - /** Closure $closure */ + public function listen(string $class, string $value, $closure) { } } From 725d998ef7363ffbe42253102a006b20f8c2afb1 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sat, 3 Feb 2024 00:28:46 -0300 Subject: [PATCH 21/42] Move SignatureMethodService to TokenService Signed-off-by: Vitor Mattos --- lib/Controller/PageController.php | 13 ++--- lib/Controller/SignFileController.php | 2 - lib/Helper/ValidateHelper.php | 2 - .../IdentifyMethod/AbstractIdentifyMethod.php | 22 +++----- lib/Service/IdentifyMethod/Account.php | 1 - lib/Service/IdentifyMethod/Email.php | 23 ++++++-- .../IdentifyMethod/IIdentifyMethod.php | 5 ++ .../AbstractSignatureMethod.php | 6 +++ .../SignatureMethod/EmailToken.php | 9 +++- .../SignatureMethod/ISignatureMethod.php | 1 + .../SignatureMethod/Password.php | 1 - .../SignatureMethod/TokenService.php} | 52 ++++++------------- lib/Service/IdentifyMethodService.php | 24 +++++---- lib/Service/SignFileService.php | 14 ----- lib/Settings/Admin.php | 2 - .../SignPDF/_partials/ModalEmailManager.vue | 3 +- src/views/SignPDF/_partials/Sign.vue | 13 +++-- tests/Unit/Helper/ValidateHelperTest.php | 4 -- tests/Unit/Service/SignFileServiceTest.php | 4 -- tests/Unit/Settings/AdminTest.php | 4 -- 20 files changed, 90 insertions(+), 115 deletions(-) rename lib/Service/{SignatureMethodService.php => IdentifyMethod/SignatureMethod/TokenService.php} (78%) diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 7081e8f5e7..a663446f2d 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -33,10 +33,11 @@ use OCA\Libresign\Middleware\Attribute\RequireSignRequestUuid; use OCA\Libresign\Service\AccountService; use OCA\Libresign\Service\FileService; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\EmailToken; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\TokenService; use OCA\Libresign\Service\IdentifyMethodService; use OCA\Libresign\Service\RequestSignatureService; use OCA\Libresign\Service\SessionService; -use OCA\Libresign\Service\SignatureMethodService; use OCA\Libresign\Service\SignFileService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; @@ -69,7 +70,6 @@ public function __construct( protected RequestSignatureService $requestSignatureService, protected IL10N $l10n, private IdentifyMethodService $identifyMethodService, - private SignatureMethodService $signatureMethodService, private IAppConfig $appConfig, private FileService $fileService, private ValidateHelper $validateHelper, @@ -148,9 +148,6 @@ public function sign($uuid): TemplateResponse { $this->getSignRequestEntity(), ) ); - $this->initialState->provideInitialState('identifyMethods', - $this->signFileService->getAvailableIdentifyMethodsFromSignRequest($this->getSignRequestEntity()) - ); $this->initialState->provideInitialState('filename', $this->getFileEntity()->getName()); $file = $this->fileService ->setFile($this->getFileEntity()) @@ -168,7 +165,7 @@ public function sign($uuid): TemplateResponse { $signatureMethods = $this->identifyMethodService->getSignMethodsOfIdentifiedFactors($this->getSignRequestEntity()->getId()); $this->provideBlurredEmail($signatureMethods, $this->userSession->getUser()?->getEMailAddress()); $this->initialState->provideInitialState('signature_methods', $signatureMethods); - $this->initialState->provideInitialState('token_length', SignatureMethodService::TOKEN_LENGTH); + $this->initialState->provideInitialState('token_length', TokenService::TOKEN_LENGTH); $this->initialState->provideInitialState('description', $this->getSignRequestEntity()->getDescription() ?? ''); $this->initialState->provideInitialState('pdf', $this->signFileService->getFileUrl('url', $this->getFileEntity(), $this->getNextcloudFile(), $uuid) @@ -197,7 +194,7 @@ private function provideSignerSignatues(): void { private function provideBlurredEmail(array $signatureMethods, ?string $email): void { if (empty($email)) { foreach ($signatureMethods as $id => $method) { - if ($id === IdentifyMethodService::IDENTIFY_EMAIL) { + if ($id === EmailToken::getId()) { $identifyMethods = $this->identifyMethodService->getIdentifyMethodsFromSignRequestId($this->getSignRequestEntity()->getId()); if (isset($identifyMethods[IdentifyMethodService::IDENTIFY_EMAIL])) { $method = current($identifyMethods[IdentifyMethodService::IDENTIFY_EMAIL]); @@ -258,7 +255,7 @@ public function signAccountFile($uuid): TemplateResponse { $signatureMethods = $this->identifyMethodService->getSignMethodsOfIdentifiedFactors($this->getSignRequestEntity()->getId()); $this->provideBlurredEmail($signatureMethods, $this->userSession->getUser()?->getEMailAddress()); $this->initialState->provideInitialState('signature_methods', $signatureMethods); - $this->initialState->provideInitialState('token_length', SignatureMethodService::TOKEN_LENGTH); + $this->initialState->provideInitialState('token_length', TokenService::TOKEN_LENGTH); $this->initialState->provideInitialState('description', ''); $nextcloudFile = $this->signFileService->getNextcloudFile($fileEntity->getNodeId()); $this->initialState->provideInitialState('pdf', diff --git a/lib/Controller/SignFileController.php b/lib/Controller/SignFileController.php index 134fdd915d..5c8bdaf1ef 100644 --- a/lib/Controller/SignFileController.php +++ b/lib/Controller/SignFileController.php @@ -34,7 +34,6 @@ use OCA\Libresign\Middleware\Attribute\RequireManager; use OCA\Libresign\Middleware\Attribute\RequireSigner; use OCA\Libresign\Service\FileService; -use OCA\Libresign\Service\SignatureMethodService; use OCA\Libresign\Service\SignFileService; use OCA\TwoFactorGateway\Exception\SmsTransmissionException; use OCP\AppFramework\Http; @@ -57,7 +56,6 @@ public function __construct( protected IUserSession $userSession, private ValidateHelper $validateHelper, protected SignFileService $signFileService, - protected SignatureMethodService $signatureMethodService, private FileService $fileService, protected LoggerInterface $logger ) { diff --git a/lib/Helper/ValidateHelper.php b/lib/Helper/ValidateHelper.php index 6a4c23742d..657112b356 100644 --- a/lib/Helper/ValidateHelper.php +++ b/lib/Helper/ValidateHelper.php @@ -40,7 +40,6 @@ use OCA\Libresign\Exception\LibresignException; use OCA\Libresign\Service\FileService; use OCA\Libresign\Service\IdentifyMethodService; -use OCA\Libresign\Service\SignatureMethodService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Services\IAppConfig; use OCP\Files\Config\IUserMountCache; @@ -78,7 +77,6 @@ public function __construct( private UserElementMapper $userElementMapper, private IdentifyMethodMapper $identifyMethodMapper, private IdentifyMethodService $identifyMethodService, - private SignatureMethodService $signatureMethodService, private IMimeTypeDetector $mimeTypeDetector, private IHasher $hasher, private IAppConfig $appConfig, diff --git a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php index 79862ee602..be81f9b271 100644 --- a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php @@ -35,7 +35,6 @@ use Wobeto\EmailBlur\Blur; abstract class AbstractIdentifyMethod implements IIdentifyMethod { - protected bool $canCreateAccount = true; protected IdentifyMethod $entity; protected string $name; protected string $friendlyName; @@ -55,6 +54,11 @@ public function __construct( $this->cleanEntity(); } + public static function getId(): string { + $id = lcfirst(substr(strrchr(get_called_class(), '\\'), 1)); + return $id; + } + public function getName(): string { return $this->name; } @@ -224,21 +228,6 @@ protected function throwIfRenewalIntervalExpired(): void { } } - protected function throwIfNeedToCreateAccount() { - if (!$this->canCreateAccount) { - return; - } - if ($this->identifyMethodService->getSessionService()->getSignStartTime()) { - return; - } - $email = $this->getEntity()->getIdentifierValue(); - throw new LibresignException(json_encode([ - 'action' => JSActions::ACTION_CREATE_USER, - 'settings' => ['accountHash' => md5($email)], - 'message' => $this->identifyMethodService->getL10n()->t('You need to create an account to sign this file.'), - ])); - } - private function getRenewAction(): int { switch ($this->name) { case 'email': @@ -294,6 +283,7 @@ private function loadSavedSettings(): void { return $carry; }, []); foreach ($this->settings['signatureMethods'] as $method => $settings) { + $this->signatureMethods[$method]->setEntity($this->getEntity()); if (is_object($this->signatureMethods[$method]) && isset($settings['enabled']) && $settings['enabled']) { $this->signatureMethods[$method]->enable(); } diff --git a/lib/Service/IdentifyMethod/Account.php b/lib/Service/IdentifyMethod/Account.php index 90dcda0c99..7e3f15b929 100644 --- a/lib/Service/IdentifyMethod/Account.php +++ b/lib/Service/IdentifyMethod/Account.php @@ -71,7 +71,6 @@ public function __construct( parent::__construct( $identifyMethodService, ); - $this->getSettings(); } public function notify(bool $isNew): void { diff --git a/lib/Service/IdentifyMethod/Email.php b/lib/Service/IdentifyMethod/Email.php index 35ac971152..410b0e3312 100644 --- a/lib/Service/IdentifyMethod/Email.php +++ b/lib/Service/IdentifyMethod/Email.php @@ -57,7 +57,6 @@ public function __construct( parent::__construct( $identifyMethodService, ); - $this->getSettings(); } public function notify(bool $isNew): void { @@ -89,6 +88,22 @@ public function validateToIdentify(): void { $this->updateIdentifiedAt(); } + protected function throwIfNeedToCreateAccount() { + $settings = $this->getSettings(); + if (!$settings['can_create_account']) { + return; + } + if ($this->identifyMethodService->getSessionService()->getSignStartTime()) { + return; + } + $email = $this->getEntity()->getIdentifierValue(); + throw new LibresignException(json_encode([ + 'action' => JSActions::ACTION_CREATE_USER, + 'settings' => ['accountHash' => md5($email)], + 'message' => $this->identifyMethodService->getL10n()->t('You need to create an account to sign this file.'), + ])); + } + private function throwIfIsAuthenticatedWithDifferentAccount(?IUser $user): void { if (!$user instanceof IUser) { return; @@ -144,7 +159,8 @@ public function validateToCreateAccount(string $value): void { } private function throwIfNotAllowedToCreateAccount(): void { - if (!$this->canCreateAccount) { + $settings = $this->getSettings(); + if (!$settings['can_create_account']) { throw new LibresignException(json_encode([ 'action' => JSActions::ACTION_SHOW_ERROR, 'errors' => [$this->identifyMethodService->getL10n()->t('It is not possible to create new accounts.')], @@ -165,13 +181,12 @@ public function getSettings(): array { $this->settings = parent::getSettingsFromDatabase( default: [ 'enabled' => false, - 'can_create_account' => $this->canCreateAccount, + 'can_create_account' => true, ], immutable: [ 'test_url' => $this->identifyMethodService->getUrlGenerator()->linkToRoute('settings.MailSettings.sendTestMail'), ] ); - $this->canCreateAccount = $this->settings['can_create_account']; return $this->settings; } } diff --git a/lib/Service/IdentifyMethod/IIdentifyMethod.php b/lib/Service/IdentifyMethod/IIdentifyMethod.php index 81f666c2d7..52a15b0493 100644 --- a/lib/Service/IdentifyMethod/IIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/IIdentifyMethod.php @@ -25,9 +25,11 @@ namespace OCA\Libresign\Service\IdentifyMethod; use OCA\Libresign\Db\IdentifyMethod; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\AbstractSignatureMethod; use OCP\IUser; interface IIdentifyMethod { + public static function getId(): string; public function getName(): string; public function getFriendlyName(): string; public function setCodeSentByUser(string $code): void; @@ -35,6 +37,9 @@ public function setUser(?IUser $user): void; public function cleanEntity(): void; public function setEntity(IdentifyMethod $entity): void; public function getEntity(): IdentifyMethod; + /** + * @return AbstractSignatureMethod[] + */ public function getSignatureMethods(): array; public function signatureMethodsToArray(): array; public function getSettings(): array; diff --git a/lib/Service/IdentifyMethod/SignatureMethod/AbstractSignatureMethod.php b/lib/Service/IdentifyMethod/SignatureMethod/AbstractSignatureMethod.php index ed264965a5..36cfbc598f 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/AbstractSignatureMethod.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/AbstractSignatureMethod.php @@ -36,4 +36,10 @@ public function enable(): void { public function isEnabled(): bool { return $this->enabled; } + + public function toArray(): array { + return [ + 'label' => $this->getFriendlyName(), + ]; + } } diff --git a/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php index 8f5fb83b76..2efd1c5143 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php @@ -27,9 +27,9 @@ use OCA\Libresign\Service\IdentifyMethod\IdentifyMethodService; class EmailToken extends AbstractSignatureMethod { - public const ID = 'email'; public function __construct( protected IdentifyMethodService $identifyMethodService, + protected TokenService $tokenService, ) { // TRANSLATORS Name of possible authenticator method. This signalize that the signer could be identified by email $this->friendlyName = $this->identifyMethodService->getL10n()->t('Email token'); @@ -37,4 +37,11 @@ public function __construct( $identifyMethodService, ); } + + public function toArray(): array { + $return = parent::toArray(); + $entity = $this->getEntity(); + $return['needCode'] = empty($entity->getCode()); + return $return; + } } diff --git a/lib/Service/IdentifyMethod/SignatureMethod/ISignatureMethod.php b/lib/Service/IdentifyMethod/SignatureMethod/ISignatureMethod.php index 715af4ea4c..45dc328664 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/ISignatureMethod.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/ISignatureMethod.php @@ -27,4 +27,5 @@ interface ISignatureMethod { public function enable(): void; public function isEnabled(): bool; + public function toArray(): array; } diff --git a/lib/Service/IdentifyMethod/SignatureMethod/Password.php b/lib/Service/IdentifyMethod/SignatureMethod/Password.php index 307b80aab0..51b108e5e5 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/Password.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/Password.php @@ -29,7 +29,6 @@ use OCA\Libresign\Service\IdentifyMethod\IdentifyMethodService; class Password extends AbstractSignatureMethod { - public const ID = 'password'; public function __construct( protected IdentifyMethodService $identifyMethodService, protected Pkcs12Handler $pkcs12Handler, diff --git a/lib/Service/SignatureMethodService.php b/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php similarity index 78% rename from lib/Service/SignatureMethodService.php rename to lib/Service/IdentifyMethod/SignatureMethod/TokenService.php index 7e516fd140..847e9fe51e 100644 --- a/lib/Service/SignatureMethodService.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php @@ -22,15 +22,14 @@ * along with this program. If not, see . */ -namespace OCA\Libresign\Service; +namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; use OCA\Libresign\Db\SignRequest; use OCA\Libresign\Exception\LibresignException; use OCA\Libresign\Service\IdentifyMethod\AbstractIdentifyMethod; use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod; -use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\ClickToSign; -use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\EmailToken; -use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\Password; +use OCA\Libresign\Service\IdentifyMethodService; +use OCA\Libresign\Service\MailService; use OCP\Accounts\IAccountManager; use OCP\App\IAppManager; use OCP\AppFramework\OCS\OCSForbiddenException; @@ -41,7 +40,7 @@ use OCP\Security\ISecureRandom; use Psr\Container\ContainerInterface; -class SignatureMethodService { +class TokenService { public const TOKEN_LENGTH = 6; private const SIGN_PASSWORD = 'password'; private const SIGN_SIGNAL = 'signal'; @@ -54,32 +53,15 @@ class SignatureMethodService { private array $methods; public function __construct( - private IdentifyMethodService $identifyMethodService, - private IAccountManager $accountManager, - private IAppManager $appManager, - private IL10N $l10n, - private ISecureRandom $secureRandom, - private IHasher $hasher, - private ContainerInterface $serverContainer, - private MailService $mail, - private Password $password, - private ClickToSign $clickToSign, - private EmailToken $email, + // private IdentifyMethodService $identifyMethodService, + // private IAccountManager $accountManager, + // private IAppManager $appManager, + // private IL10N $l10n, + // private ISecureRandom $secureRandom, + // private IHasher $hasher, + // private ContainerInterface $serverContainer, + // private MailService $mail, ) { - $this->methods = [ - $this->password->getName() => $this->password, - $this->clickToSign->getName() => $this->clickToSign, - $this->email->getName() => $this->email, - ]; - } - - public function getMethods(): array { - return array_map(function (AbstractIdentifyMethod $method) { - return [ - 'label' => $method->friendlyName, - 'enabled' => $method->isEnabledAsSignatueMethod(), - ]; - }, $this->methods); } public function requestCode(SignRequest $signRequest, string $methodId, string $identify = ''): string { @@ -116,15 +98,15 @@ public function requestCode(SignRequest $signRequest, string $methodId, string $ private function sendCode(SignRequest $signRequest, string $methodId, string $code, string $identify = ''): void { switch ($methodId) { - case SignatureMethodService::SIGN_SMS: - case SignatureMethodService::SIGN_TELEGRAM: - case SignatureMethodService::SIGN_SIGNAL: + case TokenService::SIGN_SMS: + case TokenService::SIGN_TELEGRAM: + case TokenService::SIGN_SIGNAL: $this->sendCodeByGateway($code, gatewayName: $methodId); break; - case SignatureMethodService::SIGN_EMAIL: + case TokenService::SIGN_EMAIL: $this->sendCodeByEmail($code, $identify, $signRequest->getDisplayName()); break; - case SignatureMethodService::SIGN_PASSWORD: + case TokenService::SIGN_PASSWORD: throw new LibresignException($this->l10n->t('Sending authorization code not enabled.')); } } diff --git a/lib/Service/IdentifyMethodService.php b/lib/Service/IdentifyMethodService.php index 6f466f8391..126e47de56 100644 --- a/lib/Service/IdentifyMethodService.php +++ b/lib/Service/IdentifyMethodService.php @@ -24,6 +24,7 @@ namespace OCA\Libresign\Service; +use OCA\Libresign\Db\IdentifyMethod; use OCA\Libresign\Db\IdentifyMethodMapper; use OCA\Libresign\Db\SignRequest; use OCA\Libresign\Exception\LibresignException; @@ -50,6 +51,7 @@ class IdentifyMethodService { self::IDENTIFY_PASSWORD, self::IDENTIFY_CLICK_TO_SIGN, ]; + private ?IdentifyMethod $currentIdentifyMethod = null; private array $identifyMethodsSettings = []; /** * @var array> @@ -76,9 +78,11 @@ public function getInstanceOfIdentifyMethod(string $name, ?string $identifyValue $identifyMethod = $this->getNewInstanceOfMethod($name); $entity = $identifyMethod->getEntity(); - $entity->setIdentifierKey($name); - $entity->setIdentifierValue($identifyValue); - $entity->setMandatory($this->isMandatoryMethod($name) ? 1 : 0); + if (!$entity->getId()) { + $entity->setIdentifierKey($name); + $entity->setIdentifierValue($identifyValue); + $entity->setMandatory($this->isMandatoryMethod($name) ? 1 : 0); + } if ($identifyValue) { $identifyMethod->validateToRequest(); } @@ -90,7 +94,11 @@ public function getInstanceOfIdentifyMethod(string $name, ?string $identifyValue private function getNewInstanceOfMethod(string $name): IIdentifyMethod { $className = 'OCA\Libresign\Service\IdentifyMethod\\' . ucfirst($name); $identifyMethod = clone \OC::$server->get($className); - $identifyMethod->cleanEntity(); + if (empty($this->currentIdentifyMethod)) { + $identifyMethod->cleanEntity(); + } else { + $identifyMethod->setEntity($this->currentIdentifyMethod); + } return $identifyMethod; } @@ -141,11 +149,11 @@ public function getByUserData(array $data) { public function getIdentifyMethodsFromSignRequestId(int $signRequestId): array { $entities = $this->identifyMethodMapper->getIdentifyMethodsFromSignRequestId($signRequestId); foreach ($entities as $entity) { - $identifyMethod = $this->getInstanceOfIdentifyMethod( + $this->currentIdentifyMethod = $entity; + $this->getInstanceOfIdentifyMethod( $entity->getIdentifierKey(), $entity->getIdentifierValue(), ); - $identifyMethod->setEntity($entity); } $return = []; foreach ($this->identifyMethods as $methodName => $list) { @@ -170,9 +178,7 @@ public function getSignMethodsOfIdentifiedFactors(int $signRequestId): array { if (!$signatureMethod->isEnabled()) { continue; } - $return[$signatureMethod->getName()] = [ - 'label' => $signatureMethod->getFriendlyName(), - ]; + $return[$signatureMethod->getName()] = $signatureMethod->toArray(); } } } diff --git a/lib/Service/SignFileService.php b/lib/Service/SignFileService.php index a6a45d07b4..20c568b83f 100644 --- a/lib/Service/SignFileService.php +++ b/lib/Service/SignFileService.php @@ -103,7 +103,6 @@ public function __construct( private UserElementMapper $userElementMapper, private IEventDispatcher $eventDispatcher, private IURLGenerator $urlGenerator, - private SignatureMethodService $signMethod, private IdentifyMethodMapper $identifyMethodMapper, private ITempManager $tempManager, private IdentifyMethodService $identifyMethodService, @@ -611,19 +610,6 @@ public function getSignerData(?IUser $user, ?SignRequestEntity $signRequest = nu return $return; } - public function getAvailableIdentifyMethodsFromSignRequest(SignRequestEntity $signRequest): array { - $identifyMethods = $this->identifyMethodMapper->getIdentifyMethodsFromSignRequestId($signRequest->getId()); - $return = array_map(function (IdentifyMethod $identifyMethod): array { - return [ - 'mandatory' => $identifyMethod->getMandatory(), - 'identifiedAtDate' => $identifyMethod->getIdentifiedAtDate(), - 'validateCode' => $identifyMethod->getCode() && empty($identifyMethod->getIdentifiedAtDate()) ? true : false, - 'method' => $identifyMethod->getIdentifierKey(), - ]; - }, $identifyMethods); - return $return; - } - public function getAvailableIdentifyMethodsFromSettings(): array { $identifyMethods = $this->identifyMethodService->getIdentifyMethodsSettings(); $return = array_map(function (array $identifyMethod): array { diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index fbef8826af..4206fbfc92 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -27,7 +27,6 @@ use OCA\Libresign\AppInfo\Application; use OCA\Libresign\Handler\CertificateEngine\Handler as CertificateEngineHandler; use OCA\Libresign\Service\IdentifyMethodService; -use OCA\Libresign\Service\SignatureMethodService; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IAppConfig; use OCP\AppFramework\Services\IInitialState; @@ -40,7 +39,6 @@ public function __construct( private IdentifyMethodService $identifyMethodService, private CertificateEngineHandler $certificateEngineHandler, private IAppConfig $appConfig, - private SignatureMethodService $SignatureMethodService, ) { } public function getForm(): TemplateResponse { diff --git a/src/views/SignPDF/_partials/ModalEmailManager.vue b/src/views/SignPDF/_partials/ModalEmailManager.vue index 0e4e72c841..ec22a918d2 100644 --- a/src/views/SignPDF/_partials/ModalEmailManager.vue +++ b/src/views/SignPDF/_partials/ModalEmailManager.vue @@ -34,7 +34,8 @@ {{ t('libresign', 'Request new code') }} - + diff --git a/src/views/SignPDF/_partials/Sign.vue b/src/views/SignPDF/_partials/Sign.vue index 9685d2a81d..08460c9580 100644 --- a/src/views/SignPDF/_partials/Sign.vue +++ b/src/views/SignPDF/_partials/Sign.vue @@ -100,11 +100,11 @@ @update:phone="val => $emit('update:phone', val)" @close="onModalClose('sms')" /> - @@ -211,15 +211,15 @@ export default { && !this.hasSignatures }, needEmailCode() { - return Object.hasOwn(this.signatureMethods, 'email') - && this.signatureMethods.email.validateCode + return Object.hasOwn(this.signatureMethods, 'emailToken') + && this.signatureMethods.emailToken.needCode }, needClickToSign() { return Object.hasOwn(this.signatureMethods, 'clickToSign') }, needSmsCode() { return Object.hasOwn(this.signatureMethods, 'sms') - && this.signatureMethods.sms.validateCode + && this.signatureMethods.sms.needCode }, ableToSign() { if (this.needCreatePassword) { @@ -332,7 +332,7 @@ export default { }, confirmSignDocument() { if (this.needEmailCode) { - this.signatureMethods.email.modal = true + this.signatureMethods.emailToken.modal = true return } if (this.needSignature) { @@ -344,7 +344,6 @@ export default { return } if (Object.hasOwn(this.signatureMethods, 'password') - && this.signatureMethods.password.enabled && !this.needCreatePassword ) { this.modalSignWithPassword = true diff --git a/tests/Unit/Helper/ValidateHelperTest.php b/tests/Unit/Helper/ValidateHelperTest.php index 5c7363e0c1..98559bedbf 100644 --- a/tests/Unit/Helper/ValidateHelperTest.php +++ b/tests/Unit/Helper/ValidateHelperTest.php @@ -13,7 +13,6 @@ use OCA\Libresign\Exception\LibresignException; use OCA\Libresign\Helper\ValidateHelper; use OCA\Libresign\Service\IdentifyMethodService; -use OCA\Libresign\Service\SignatureMethodService; use OCP\AppFramework\Services\IAppConfig; use OCP\Files\Config\IUserMountCache; use OCP\Files\IMimeTypeDetector; @@ -35,7 +34,6 @@ final class ValidateHelperTest extends \OCA\Libresign\Tests\Unit\TestCase { private UserElementMapper|MockObject $userElementMapper; private IdentifyMethodMapper|MockObject $identifyMethodMapper; private IdentifyMethodService $identifyMethodService; - private SignatureMethodService|MockObject $signatureMethodService; private IMimeTypeDetector $mimeTypeDetector; private IHasher $hasher; private IAppConfig|MockObject $appConfig; @@ -57,7 +55,6 @@ public function setUp(): void { $this->userElementMapper = $this->createMock(UserElementMapper::class); $this->identifyMethodMapper = $this->createMock(IdentifyMethodMapper::class); $this->identifyMethodService = $this->createMock(IdentifyMethodService::class); - $this->signatureMethodService = $this->createMock(SignatureMethodService::class); $this->mimeTypeDetector = \OC::$server->get(IMimeTypeDetector::class); $this->hasher = $this->createMock(IHasher::class); $this->appConfig = $this->createMock(IAppConfig::class); @@ -78,7 +75,6 @@ private function getValidateHelper(): ValidateHelper { $this->userElementMapper, $this->identifyMethodMapper, $this->identifyMethodService, - $this->signatureMethodService, $this->mimeTypeDetector, $this->hasher, $this->appConfig, diff --git a/tests/Unit/Service/SignFileServiceTest.php b/tests/Unit/Service/SignFileServiceTest.php index 09bf4bf3bd..a8a8ddbe22 100644 --- a/tests/Unit/Service/SignFileServiceTest.php +++ b/tests/Unit/Service/SignFileServiceTest.php @@ -11,7 +11,6 @@ use OCA\Libresign\Helper\ValidateHelper; use OCA\Libresign\Service\FolderService; use OCA\Libresign\Service\IdentifyMethodService; -use OCA\Libresign\Service\SignatureMethodService; use OCA\Libresign\Service\SignFileService; use OCP\AppFramework\Services\IAppConfig; use OCP\AppFramework\Utility\ITimeFactory; @@ -50,7 +49,6 @@ final class SignFileServiceTest extends \OCA\Libresign\Tests\Unit\TestCase { private UserElementMapper|MockObject $userElementMapper; private IEventDispatcher|MockObject $eventDispatcher; private IURLGenerator|MockObject $urlGenerator; - private SignatureMethodService|MockObject $signMethod; private IdentifyMethodMapper|MockObject $identifyMethodMapper; private ITempManager|MockObject $tempManager; private IdentifyMethodService $identifyMethodService; @@ -80,7 +78,6 @@ public function setUp(): void { $this->userElementMapper = $this->createMock(UserElementMapper::class); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->urlGenerator = $this->createMock(IURLGenerator::class); - $this->signMethod = $this->createMock(SignatureMethodService::class); $this->identifyMethodMapper = $this->createMock(IdentifyMethodMapper::class); $this->tempManager = $this->createMock(ITempManager::class); $this->identifyMethodService = $this->createMock(IdentifyMethodService::class); @@ -108,7 +105,6 @@ private function getService(): SignFileService { $this->userElementMapper, $this->eventDispatcher, $this->urlGenerator, - $this->signMethod, $this->identifyMethodMapper, $this->tempManager, $this->identifyMethodService, diff --git a/tests/Unit/Settings/AdminTest.php b/tests/Unit/Settings/AdminTest.php index 96a51e2734..ed52b3ad54 100644 --- a/tests/Unit/Settings/AdminTest.php +++ b/tests/Unit/Settings/AdminTest.php @@ -5,7 +5,6 @@ use OCA\Libresign\AppInfo\Application; use OCA\Libresign\Handler\CertificateEngine\Handler as CertificateEngineHandler; use OCA\Libresign\Service\IdentifyMethodService; -use OCA\Libresign\Service\SignatureMethodService; use OCA\Libresign\Settings\Admin; use OCP\AppFramework\Services\IAppConfig; use OCP\AppFramework\Services\IInitialState; @@ -20,19 +19,16 @@ final class AdminTest extends \OCA\Libresign\Tests\Unit\TestCase { private IdentifyMethodService|MockObject $identifyMethodService; private CertificateEngineHandler|MockObject $certificateEngineHandler; private IAppConfig|MockObject $appConfig; - private SignatureMethodService|MockObject $signatureMethodService; public function setUp(): void { $this->initialState = $this->createMock(IInitialState::class); $this->identifyMethodService = $this->createMock(IdentifyMethodService::class); $this->certificateEngineHandler = $this->createMock(CertificateEngineHandler::class); $this->appConfig = $this->createMock(IAppConfig::class); - $this->signatureMethodService = $this->createMock(SignatureMethodService::class); $this->admin = new Admin( $this->initialState, $this->identifyMethodService, $this->certificateEngineHandler, $this->appConfig, - $this->signatureMethodService ); } From 87833c15a1bab772c3511fcff52cabddd5b7dbb7 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sat, 3 Feb 2024 01:28:49 -0300 Subject: [PATCH 22/42] Task: sign using the visible elements. Signed-off-by: Vitor Mattos --- lib/Controller/SignFileController.php | 5 +- lib/Helper/ValidateHelper.php | 8 -- .../SignatureMethod/EmailToken.php | 14 +- .../IdentifyMethod/SignatureMethod/IToken.php | 29 ++++ .../SignatureMethod/TokenService.php | 127 +++++------------- lib/Service/SignFileService.php | 23 +++- .../SignPDF/_partials/ModalEmailManager.vue | 6 +- 7 files changed, 104 insertions(+), 108 deletions(-) create mode 100644 lib/Service/IdentifyMethod/SignatureMethod/IToken.php diff --git a/lib/Controller/SignFileController.php b/lib/Controller/SignFileController.php index 5c8bdaf1ef..a17b3f706b 100644 --- a/lib/Controller/SignFileController.php +++ b/lib/Controller/SignFileController.php @@ -168,6 +168,7 @@ public function signRenew(string $method): JSONResponse { #[NoAdminRequired] #[NoCSRFRequired] #[RequireSigner] + #[PublicPage] public function getCodeUsingUuid(string $uuid): JSONResponse { return $this->getCode($uuid); } @@ -190,12 +191,12 @@ private function getCode(string $uuid = null, int $fileId = null): JSONResponse } catch (\Throwable $th) { throw new LibresignException($this->l10n->t('Invalid data to sign file'), 1); } - $this->validateHelper->canRequestCode(); $libreSignFile = $this->fileMapper->getById($signRequest->getFileId()); $this->validateHelper->fileCanBeSigned($libreSignFile); $this->signFileService->requestCode( signRequest: $signRequest, - method: $this->request->getParam('method', ''), + identifyMethodName: $this->request->getParam('identifyMethod', ''), + signMethodName: $this->request->getParam('signMethod', ''), identify: $this->request->getParam('identify', ''), ); $message = $this->l10n->t('The code to sign file was successfully requested.'); diff --git a/lib/Helper/ValidateHelper.php b/lib/Helper/ValidateHelper.php index 657112b356..088c60474d 100644 --- a/lib/Helper/ValidateHelper.php +++ b/lib/Helper/ValidateHelper.php @@ -691,14 +691,6 @@ public function validateUserHasNoFileWithThisType(string $uid, string $type): vo } } - public function canRequestCode(): void { - // @todo make the sign method to say if he can request code - $signatureMethods = $this->signatureMethodService->getMethods(); - if (!array_key_exists('email', $signatureMethods)) { - throw new LibresignException($this->l10n->t('You do not have permission for this action.')); - } - } - public function canSignWithIdentificationDocumentStatus(IUser $user, int $status): void { // User that can approve validation documents don't need to have a valid // document attached to their profile. If this were required, nobody diff --git a/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php index 2efd1c5143..7753860ca8 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php @@ -26,7 +26,7 @@ use OCA\Libresign\Service\IdentifyMethod\IdentifyMethodService; -class EmailToken extends AbstractSignatureMethod { +class EmailToken extends AbstractSignatureMethod implements IToken { public function __construct( protected IdentifyMethodService $identifyMethodService, protected TokenService $tokenService, @@ -44,4 +44,16 @@ public function toArray(): array { $return['needCode'] = empty($entity->getCode()); return $return; } + + public function requestCode(string $identify): void { + $signRequestMapper = $this->identifyMethodService->getSignRequestMapper(); + $signRequest = $signRequestMapper->getById($this->getEntity()->getSignRequestId()); + $displayName = $signRequest->getDisplayName(); + if ($identify === $displayName) { + $displayName = ''; + } + $code = $this->tokenService->sendCodeByEmail($identify, $displayName); + $this->getEntity()->setCode($code); + $this->identifyMethodService->save($this->getEntity()); + } } diff --git a/lib/Service/IdentifyMethod/SignatureMethod/IToken.php b/lib/Service/IdentifyMethod/SignatureMethod/IToken.php new file mode 100644 index 0000000000..1526542f8d --- /dev/null +++ b/lib/Service/IdentifyMethod/SignatureMethod/IToken.php @@ -0,0 +1,29 @@ + + * + * @author Vitor Mattos + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; + +interface IToken { + public function requestCode(string $identify): void; +} diff --git a/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php b/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php index 847e9fe51e..a82cce5a17 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php @@ -24,122 +24,63 @@ namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; -use OCA\Libresign\Db\SignRequest; -use OCA\Libresign\Exception\LibresignException; -use OCA\Libresign\Service\IdentifyMethod\AbstractIdentifyMethod; -use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod; -use OCA\Libresign\Service\IdentifyMethodService; use OCA\Libresign\Service\MailService; use OCP\Accounts\IAccountManager; -use OCP\App\IAppManager; use OCP\AppFramework\OCS\OCSForbiddenException; -use OCP\IL10N; use OCP\IUser; use OCP\IUserSession; use OCP\Security\IHasher; use OCP\Security\ISecureRandom; -use Psr\Container\ContainerInterface; class TokenService { public const TOKEN_LENGTH = 6; - private const SIGN_PASSWORD = 'password'; - private const SIGN_SIGNAL = 'signal'; - private const SIGN_TELEGRAM = 'telegram'; - private const SIGN_SMS = 'sms'; - private const SIGN_EMAIL = 'email'; - /** - * @var AbstractIdentifyMethod[] - */ - private array $methods; + public const SIGN_PASSWORD = 'password'; + public const SIGN_SIGNAL = 'signal'; + public const SIGN_TELEGRAM = 'telegram'; + public const SIGN_SMS = 'sms'; + public const SIGN_EMAIL = 'email'; public function __construct( - // private IdentifyMethodService $identifyMethodService, - // private IAccountManager $accountManager, - // private IAppManager $appManager, - // private IL10N $l10n, - // private ISecureRandom $secureRandom, - // private IHasher $hasher, - // private ContainerInterface $serverContainer, - // private MailService $mail, + private ISecureRandom $secureRandom, + private IHasher $hasher, + private MailService $mail, ) { } - public function requestCode(SignRequest $signRequest, string $methodId, string $identify = ''): string { - if (!array_key_exists($methodId, $this->methods)) { - throw new LibresignException($this->l10n->t('Invalid Sign engine.'), 400); - } - - $identifyMethods = $this->identifyMethodService->getIdentifyMethodsFromSignRequestId($signRequest->getId()); - if (!empty($identifyMethods[$methodId])) { - $method = array_filter($identifyMethods[$methodId], function (IIdentifyMethod $identifyMethod) use ($methodId) { - return $identifyMethod->getName() === $methodId; - }); - $method = current($method); - } - if (empty($method)) { - $method = $this->identifyMethodService->getInstanceOfIdentifyMethod($methodId, $identify); - } else { - if (!empty($identify) && $identify !== $method->getEntity()->getIdentifierKey()) { - $method->getEntity()->setIdentifierValue($identify); - } - $identify = $method->getEntity()->getIdentifierValue(); - } - - $token = $this->secureRandom->generate(self::TOKEN_LENGTH, ISecureRandom::CHAR_DIGITS); - $this->sendCode($signRequest, $methodId, $token, $identify); - - $entity = $method->getEntity(); - $entity->setCode($this->hasher->hash($token)); - $entity->setMandatory(0); - $this->identifyMethodService->save($signRequest, false); - - return $token; - } - - private function sendCode(SignRequest $signRequest, string $methodId, string $code, string $identify = ''): void { - switch ($methodId) { - case TokenService::SIGN_SMS: - case TokenService::SIGN_TELEGRAM: - case TokenService::SIGN_SIGNAL: - $this->sendCodeByGateway($code, gatewayName: $methodId); - break; - case TokenService::SIGN_EMAIL: - $this->sendCodeByEmail($code, $identify, $signRequest->getDisplayName()); - break; - case TokenService::SIGN_PASSWORD: - throw new LibresignException($this->l10n->t('Sending authorization code not enabled.')); - } - } - - private function sendCodeByGateway(string $code, string $gatewayName): void { - $user = \OC::$server->get(IUserSession::class)->getUser(); - $gateway = $this->getGateway($user, $gatewayName); - - $userAccount = $this->accountManager->getAccount($user); - $identifier = $userAccount->getProperty(IAccountManager::PROPERTY_PHONE)->getValue(); - $gateway->send($user, $identifier, $this->l10n->t('%s is your LibreSign verification code.', $code)); - } - /** - * @throws OCSForbiddenException + * @todo check this code and put to work */ - private function getGateway(IUser $user, string $gatewayName): \OCA\TwoFactorGateway\Service\Gateway\IGateway { - if (!$this->appManager->isEnabledForUser('twofactor_gateway', $user)) { - throw new OCSForbiddenException($this->l10n->t('Authorize signing using %s token is disabled because Nextcloud Two-Factor Gateway is not enabled.', $gatewayName)); - } - $factory = $this->serverContainer->get('\OCA\TwoFactorGateway\Service\Gateway\Factory'); - $gateway = $factory->getGateway($gatewayName); - if (!$gateway->getConfig()->isComplete()) { - throw new OCSForbiddenException($this->l10n->t('Gateway %s not configured on Two-Factor Gateway.', $gatewayName)); - } - return $gateway; + public function sendCodeByGateway(string $code, string $gatewayName): void { + // $user = \OC::$server->get(IUserSession::class)->getUser(); + // $gateway = $this->getGateway($user, $gatewayName); + + // $userAccount = $this->accountManager->getAccount($user); + // $identifier = $userAccount->getProperty(IAccountManager::PROPERTY_PHONE)->getValue(); + // $gateway->send($user, $identifier, $this->l10n->t('%s is your LibreSign verification code.', $code)); + // } + + // /** + // * @throws OCSForbiddenException + // */ + // private function getGateway(IUser $user, string $gatewayName): \OCA\TwoFactorGateway\Service\Gateway\IGateway { + // if (!$this->appManager->isEnabledForUser('twofactor_gateway', $user)) { + // throw new OCSForbiddenException($this->l10n->t('Authorize signing using %s token is disabled because Nextcloud Two-Factor Gateway is not enabled.', $gatewayName)); + // } + // $factory = $this->serverContainer->get('\OCA\TwoFactorGateway\Service\Gateway\Factory'); + // $gateway = $factory->getGateway($gatewayName); + // if (!$gateway->getConfig()->isComplete()) { + // throw new OCSForbiddenException($this->l10n->t('Gateway %s not configured on Two-Factor Gateway.', $gatewayName)); + // } + // return $gateway; } - private function sendCodeByEmail(string $code, string $email, string $displayName): void { + public function sendCodeByEmail(string $email, string $displayName): string { + $code = $this->secureRandom->generate(self::TOKEN_LENGTH, ISecureRandom::CHAR_DIGITS); $this->mail->sendCodeToSign( email: $email, name: $displayName, code: $code ); + return $this->hasher->hash($code); } } diff --git a/lib/Service/SignFileService.php b/lib/Service/SignFileService.php index 20c568b83f..7cc9f8b518 100644 --- a/lib/Service/SignFileService.php +++ b/lib/Service/SignFileService.php @@ -412,8 +412,27 @@ public function renew(SignRequestEntity $signRequest, string $method): void { }, $identifyMethods[$method]); } - public function requestCode(SignRequestEntity $signRequest, string $method, string $identify = ''): string { - return $this->signMethod->requestCode($signRequest, $method, $identify); + public function requestCode( + SignRequestEntity $signRequest, + string $identifyMethodName, + string $signMethodName, + string $identify = '' + ): void { + $identifyMethods = $this->identifyMethodService->getIdentifyMethodsFromSignRequestId($signRequest->getId()); + if (empty($identifyMethods[$identifyMethodName])) { + throw new LibresignException($this->l10n->t('Invalid identification method')); + } + foreach ($identifyMethods[$identifyMethodName] as $identifyMethod) { + $signatureMethods = $identifyMethod->getSignatureMethods(); + if (empty($signatureMethods[$signMethodName])) { + throw new LibresignException($this->l10n->t('Invalid identification method')); + } + /** @var EmailToken $signatureMethod */ + $signatureMethod = $signatureMethods[$signMethodName]; + $signatureMethod->requestCode($identify); + return; + } + throw new LibresignException($this->l10n->t('Sending authorization code not enabled.')); } public function getSignRequestToSign(FileEntity $libresignFile, IUser $user): SignRequestEntity { diff --git a/src/views/SignPDF/_partials/ModalEmailManager.vue b/src/views/SignPDF/_partials/ModalEmailManager.vue index ec22a918d2..5aecbee078 100644 --- a/src/views/SignPDF/_partials/ModalEmailManager.vue +++ b/src/views/SignPDF/_partials/ModalEmailManager.vue @@ -142,7 +142,8 @@ export default { generateOcsUrl('/apps/libresign/api/v1/sign/file_id/{fileId}/code', { fileId: this.fileId }), { identify: this.sendTo, - method: 'email', + identifyMethod: 'email', + signMethod: 'emailToken', }, ) showSuccess(data.message) @@ -151,7 +152,8 @@ export default { generateOcsUrl('/apps/libresign/api/v1/sign/uuid/{uuid}/code', { uuid: this.uuid }), { identify: this.sendTo, - method: 'email', + identifyMethod: 'email', + signMethod: 'emailToken', }, ) showSuccess(data.message) From 2a924e1eaf56a6bbd0dff89fcd68acf31143e6ed Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sat, 3 Feb 2024 01:33:12 -0300 Subject: [PATCH 23/42] Only handle signatureMethods when exists Signed-off-by: Vitor Mattos --- lib/Controller/SignFileController.php | 1 + lib/Service/IdentifyMethod/AbstractIdentifyMethod.php | 3 +++ 2 files changed, 4 insertions(+) diff --git a/lib/Controller/SignFileController.php b/lib/Controller/SignFileController.php index a17b3f706b..91a985f7da 100644 --- a/lib/Controller/SignFileController.php +++ b/lib/Controller/SignFileController.php @@ -176,6 +176,7 @@ public function getCodeUsingUuid(string $uuid): JSONResponse { #[NoAdminRequired] #[NoCSRFRequired] #[RequireSigner] + #[PublicPage] public function getCodeUsingFileId(int $fileId): JSONResponse { return $this->getCode(null, $fileId); } diff --git a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php index be81f9b271..930746a849 100644 --- a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php @@ -282,6 +282,9 @@ private function loadSavedSettings(): void { } return $carry; }, []); + if (!isset($this->settings['signatureMethods']) || !is_array($this->settings['signatureMethods'])) { + return; + } foreach ($this->settings['signatureMethods'] as $method => $settings) { $this->signatureMethods[$method]->setEntity($this->getEntity()); if (is_object($this->signatureMethods[$method]) && isset($settings['enabled']) && $settings['enabled']) { From b7edd2912e67959e12af867eea2bd969a29534ee Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sat, 3 Feb 2024 10:24:36 -0300 Subject: [PATCH 24/42] Clean class TokenService Signed-off-by: Vitor Mattos --- .../SignatureMethod/TokenService.php | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php b/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php index a82cce5a17..3e06ab020b 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php @@ -25,10 +25,6 @@ namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; use OCA\Libresign\Service\MailService; -use OCP\Accounts\IAccountManager; -use OCP\AppFramework\OCS\OCSForbiddenException; -use OCP\IUser; -use OCP\IUserSession; use OCP\Security\IHasher; use OCP\Security\ISecureRandom; @@ -51,27 +47,27 @@ public function __construct( * @todo check this code and put to work */ public function sendCodeByGateway(string $code, string $gatewayName): void { - // $user = \OC::$server->get(IUserSession::class)->getUser(); - // $gateway = $this->getGateway($user, $gatewayName); + // $user = \OC::$server->get(IUserSession::class)->getUser(); + // $gateway = $this->getGateway($user, $gatewayName); - // $userAccount = $this->accountManager->getAccount($user); - // $identifier = $userAccount->getProperty(IAccountManager::PROPERTY_PHONE)->getValue(); - // $gateway->send($user, $identifier, $this->l10n->t('%s is your LibreSign verification code.', $code)); - // } + // $userAccount = $this->accountManager->getAccount($user); + // $identifier = $userAccount->getProperty(IAccountManager::PROPERTY_PHONE)->getValue(); + // $gateway->send($user, $identifier, $this->l10n->t('%s is your LibreSign verification code.', $code)); + // } - // /** - // * @throws OCSForbiddenException - // */ - // private function getGateway(IUser $user, string $gatewayName): \OCA\TwoFactorGateway\Service\Gateway\IGateway { - // if (!$this->appManager->isEnabledForUser('twofactor_gateway', $user)) { - // throw new OCSForbiddenException($this->l10n->t('Authorize signing using %s token is disabled because Nextcloud Two-Factor Gateway is not enabled.', $gatewayName)); - // } - // $factory = $this->serverContainer->get('\OCA\TwoFactorGateway\Service\Gateway\Factory'); - // $gateway = $factory->getGateway($gatewayName); - // if (!$gateway->getConfig()->isComplete()) { - // throw new OCSForbiddenException($this->l10n->t('Gateway %s not configured on Two-Factor Gateway.', $gatewayName)); - // } - // return $gateway; + // /** + // * @throws OCSForbiddenException + // */ + // private function getGateway(IUser $user, string $gatewayName): \OCA\TwoFactorGateway\Service\Gateway\IGateway { + // if (!$this->appManager->isEnabledForUser('twofactor_gateway', $user)) { + // throw new OCSForbiddenException($this->l10n->t('Authorize signing using %s token is disabled because Nextcloud Two-Factor Gateway is not enabled.', $gatewayName)); + // } + // $factory = $this->serverContainer->get('\OCA\TwoFactorGateway\Service\Gateway\Factory'); + // $gateway = $factory->getGateway($gatewayName); + // if (!$gateway->getConfig()->isComplete()) { + // throw new OCSForbiddenException($this->l10n->t('Gateway %s not configured on Two-Factor Gateway.', $gatewayName)); + // } + // return $gateway; } public function sendCodeByEmail(string $email, string $displayName): string { From 02e9f9e9c25d3b45304324d169a1d3f50e69ecea Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sat, 3 Feb 2024 10:25:04 -0300 Subject: [PATCH 25/42] Add namespace Signed-off-by: Vitor Mattos --- lib/Service/SignFileService.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Service/SignFileService.php b/lib/Service/SignFileService.php index 7cc9f8b518..b59497b28f 100644 --- a/lib/Service/SignFileService.php +++ b/lib/Service/SignFileService.php @@ -47,6 +47,7 @@ use OCA\Libresign\Helper\JSActions; use OCA\Libresign\Helper\ValidateHelper; use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\EmailToken; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Services\IAppConfig; use OCP\AppFramework\Utility\ITimeFactory; From 2c0ac52a8346235d54f37264ffffae37f28fa930 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sat, 3 Feb 2024 13:30:27 -0300 Subject: [PATCH 26/42] Use pinia at external pages Signed-off-by: Vitor Mattos --- src/external.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/external.js b/src/external.js index 6813e5b77a..b933126796 100644 --- a/src/external.js +++ b/src/external.js @@ -24,6 +24,7 @@ import { generateFilePath } from '@nextcloud/router' import { getRequestToken } from '@nextcloud/auth' import Vue from 'vue' +import { createPinia, PiniaVuePlugin } from 'pinia' import External from './External.vue' import router from './router/router.js' @@ -51,9 +52,14 @@ Vue.prototype.n = n Vue.prototype.OC = OC Vue.prototype.OCA = OCA +Vue.use(PiniaVuePlugin) + +const pinia = createPinia() + export default new Vue({ el: '#content', router, store, + pinia, render: h => h(External), }) From 6331fb01a64347a1fab12a43c582b662731399c2 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sat, 3 Feb 2024 14:22:39 -0300 Subject: [PATCH 27/42] Implement store at Sign component Signed-off-by: Vitor Mattos --- lib/Controller/PageController.php | 23 ---- lib/Controller/SignFileController.php | 3 + .../SignatureMethod/EmailToken.php | 14 +- .../SignatureMethod/Password.php | 15 ++ package-lock.json | 6 + package.json | 1 + src/store/signMethods.js | 70 ++++++++++ .../SignPDF/_partials/ModalEmailManager.vue | 65 +++++---- src/views/SignPDF/_partials/Sign.vue | 130 +++++++----------- 9 files changed, 192 insertions(+), 135 deletions(-) create mode 100644 src/store/signMethods.js diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index a663446f2d..250de1e8c3 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -33,7 +33,6 @@ use OCA\Libresign\Middleware\Attribute\RequireSignRequestUuid; use OCA\Libresign\Service\AccountService; use OCA\Libresign\Service\FileService; -use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\EmailToken; use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\TokenService; use OCA\Libresign\Service\IdentifyMethodService; use OCA\Libresign\Service\RequestSignatureService; @@ -57,7 +56,6 @@ use OCP\IURLGenerator; use OCP\IUserSession; use OCP\Util; -use Wobeto\EmailBlur\Blur; class PageController extends AEnvironmentPageAwareController { public function __construct( @@ -163,7 +161,6 @@ public function sign($uuid): TemplateResponse { $this->initialState->provideInitialState('signers', $file['signers']); $this->provideSignerSignatues(); $signatureMethods = $this->identifyMethodService->getSignMethodsOfIdentifiedFactors($this->getSignRequestEntity()->getId()); - $this->provideBlurredEmail($signatureMethods, $this->userSession->getUser()?->getEMailAddress()); $this->initialState->provideInitialState('signature_methods', $signatureMethods); $this->initialState->provideInitialState('token_length', TokenService::TOKEN_LENGTH); $this->initialState->provideInitialState('description', $this->getSignRequestEntity()->getDescription() ?? ''); @@ -191,25 +188,6 @@ private function provideSignerSignatues(): void { $this->initialState->provideInitialState('user_signatures', $signatures); } - private function provideBlurredEmail(array $signatureMethods, ?string $email): void { - if (empty($email)) { - foreach ($signatureMethods as $id => $method) { - if ($id === EmailToken::getId()) { - $identifyMethods = $this->identifyMethodService->getIdentifyMethodsFromSignRequestId($this->getSignRequestEntity()->getId()); - if (isset($identifyMethods[IdentifyMethodService::IDENTIFY_EMAIL])) { - $method = current($identifyMethods[IdentifyMethodService::IDENTIFY_EMAIL]); - $email = $method->getEntity()->getIdentifierValue(); - break; - } - } - } - } - if (!empty($email)) { - $blur = new Blur($email); - $this->initialState->provideInitialState('blurred_email', $blur->make()); - } - } - /** * Show signature page */ @@ -253,7 +231,6 @@ public function signAccountFile($uuid): TemplateResponse { $this->initialState->provideInitialState('signers', []); $this->provideSignerSignatues(); $signatureMethods = $this->identifyMethodService->getSignMethodsOfIdentifiedFactors($this->getSignRequestEntity()->getId()); - $this->provideBlurredEmail($signatureMethods, $this->userSession->getUser()?->getEMailAddress()); $this->initialState->provideInitialState('signature_methods', $signatureMethods); $this->initialState->provideInitialState('token_length', TokenService::TOKEN_LENGTH); $this->initialState->provideInitialState('description', ''); diff --git a/lib/Controller/SignFileController.php b/lib/Controller/SignFileController.php index 91a985f7da..a9739e547a 100644 --- a/lib/Controller/SignFileController.php +++ b/lib/Controller/SignFileController.php @@ -181,6 +181,9 @@ public function getCodeUsingFileId(int $fileId): JSONResponse { return $this->getCode(null, $fileId); } + /** + * @todo validate if can request code + */ private function getCode(string $uuid = null, int $fileId = null): JSONResponse { try { try { diff --git a/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php index 7753860ca8..7b7b465d61 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php @@ -25,6 +25,7 @@ namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; use OCA\Libresign\Service\IdentifyMethod\IdentifyMethodService; +use Wobeto\EmailBlur\Blur; class EmailToken extends AbstractSignatureMethod implements IToken { public function __construct( @@ -41,10 +42,21 @@ public function __construct( public function toArray(): array { $return = parent::toArray(); $entity = $this->getEntity(); - $return['needCode'] = empty($entity->getCode()); + $return['needCode'] = empty($entity->getCode()) + || empty($entity->getIdentifiedAtDate()) + || empty($this->codeSentByUser); + $return['hasConfirmCode'] = !empty($entity->getCode()); + $return['blurredEmail'] = $this->getBlurredEmail(); + $return['hashOfEmail'] = md5($this->getEntity()->getIdentifierValue()); return $return; } + private function getBlurredEmail(): string { + $email = $this->getEntity()->getIdentifierValue(); + $blur = new Blur($email); + return $blur->make(); + } + public function requestCode(string $identify): void { $signRequestMapper = $this->identifyMethodService->getSignRequestMapper(); $signRequest = $signRequestMapper->getById($this->getEntity()->getSignRequestId()); diff --git a/lib/Service/IdentifyMethod/SignatureMethod/Password.php b/lib/Service/IdentifyMethod/SignatureMethod/Password.php index 51b108e5e5..4d7ea6f836 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/Password.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/Password.php @@ -47,4 +47,19 @@ public function validateToIdentify(): void { throw new LibresignException($this->identifyMethodService->getL10n()->t('Invalid password')); } } + + public function toArray(): array { + $return = parent::toArray(); + $return['hasSignatureFile'] = $this->hasSignatureFile(); + return $return; + } + + private function hasSignatureFile(): bool { + try { + $this->pkcs12Handler->getPfx($this->user->getUID()); + return true; + } catch (\Throwable $th) { + } + return false; + } } diff --git a/package-lock.json b/package-lock.json index 8df9a5a3be..589cdf7726 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@nextcloud/paths": "^2.1.0", "@nextcloud/router": "^3.0.0", "@nextcloud/vue": "^8.6.1", + "blueimp-md5": "^2.19.0", "crypto-js": "^4.2.0", "dompurify": "^3.0.8", "linkify-string": "^4.1.3", @@ -5870,6 +5871,11 @@ "dev": true, "peer": true }, + "node_modules/blueimp-md5": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==" + }, "node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", diff --git a/package.json b/package.json index 79db3f682f..6cf1f99d68 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@nextcloud/paths": "^2.1.0", "@nextcloud/router": "^3.0.0", "@nextcloud/vue": "^8.6.1", + "blueimp-md5": "^2.19.0", "crypto-js": "^4.2.0", "dompurify": "^3.0.8", "linkify-string": "^4.1.3", diff --git a/src/store/signMethods.js b/src/store/signMethods.js new file mode 100644 index 0000000000..be98d0b260 --- /dev/null +++ b/src/store/signMethods.js @@ -0,0 +1,70 @@ +/* + * @copyright Copyright (c) 2024 Vitor Mattos + * + * @author Vitor Mattos + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { defineStore } from 'pinia' +import { set } from 'vue' +import { loadState } from '@nextcloud/initial-state' + +export const useSignMethodsStore = defineStore('signMethods', { + state: () => ({ + modal: { + emailToken: false, + clickToSign: false, + createPassword: false, + signPassword: false, + createSignature: false, + sms: false, + }, + settings: loadState('libresign', 'signature_methods', []), + }), + actions: { + closeModal(modalCode) { + set(this.modal, modalCode, false) + }, + showModal(modalCode) { + set(this.modal, modalCode, true) + }, + blurredEmail() { + return this.settings.emailToken.blurredEmail + }, + hasEmailConfirmCode(hasConfirmCode) { + set(this.settings.emailToken, 'hasConfirmCode', hasConfirmCode) + }, + hasSignatureFile(hasSignatureFile) { + set(this.signMethodsStore.settings.password, 'hasSignatureFile', hasSignatureFile) + }, + needCreatePassword() { + return Object.hasOwn(this.settings, 'password') + && !this.settings.password.hasSignatureFile + }, + needEmailCode() { + return Object.hasOwn(this.settings, 'emailToken') + && this.settings.emailToken.needCode + }, + needClickToSign() { + return Object.hasOwn(this.settings, 'clickToSign') + }, + needSmsCode() { + return Object.hasOwn(this.settings, 'sms') + && this.settings.sms.needCode + }, + }, +}) diff --git a/src/views/SignPDF/_partials/ModalEmailManager.vue b/src/views/SignPDF/_partials/ModalEmailManager.vue index 5aecbee078..bf69c8fdb9 100644 --- a/src/views/SignPDF/_partials/ModalEmailManager.vue +++ b/src/views/SignPDF/_partials/ModalEmailManager.vue @@ -8,10 +8,10 @@
-