From 36d259c523bbd54c170e62de0b8fd509bb1cf04c Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Mon, 22 Sep 2025 17:05:55 -0300 Subject: [PATCH 01/51] chore: stop search early if haven't values to search for Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Controller/IdentifyAccountController.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/Controller/IdentifyAccountController.php b/lib/Controller/IdentifyAccountController.php index 8b494ac00f..07aab7a6ca 100644 --- a/lib/Controller/IdentifyAccountController.php +++ b/lib/Controller/IdentifyAccountController.php @@ -56,15 +56,12 @@ public function __construct( #[RequireManager] #[ApiRoute(verb: 'GET', url: '/api/{apiVersion}/identify-account/search', requirements: ['apiVersion' => '(v1)'])] public function search(string $search = '', int $page = 1, int $limit = 25): DataResponse { - $shareTypes = $this->getShareTypes(); - $lookup = false; - - // only search for string larger than a given threshold - $threshold = 1; - if (strlen($search) < $threshold) { + // only search for string larger than a minimum length + if (strlen($search) < 1) { return new DataResponse(); } + $lookup = false; $offset = $limit * ($page - 1); [$result] = $this->collaboratorSearch->search($search, $shareTypes, $lookup, $limit, $offset); $result['exact'] = $this->unifyResult($result['exact']); From 4c843ae84db1989c2f8c5d622a0d29ea991b81d8 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Mon, 22 Sep 2025 17:11:38 -0300 Subject: [PATCH 02/51] feat: Search by signers Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../Collaborators/SignerPlugin.php | 100 ++++++++++++++++++ lib/Controller/IdentifyAccountController.php | 43 ++++++-- lib/Db/IdentifyMethodMapper.php | 59 +++++++++++ 3 files changed, 196 insertions(+), 6 deletions(-) create mode 100644 lib/Collaboration/Collaborators/SignerPlugin.php diff --git a/lib/Collaboration/Collaborators/SignerPlugin.php b/lib/Collaboration/Collaborators/SignerPlugin.php new file mode 100644 index 0000000000..d3dacb9eba --- /dev/null +++ b/lib/Collaboration/Collaborators/SignerPlugin.php @@ -0,0 +1,100 @@ +userSession->getUser()->getUID(); + + $limit++; + $identifiers = $this->identifyMethodMapper->searchByIdentifierValue( + $search, + $user, + self::$method, + $limit, + $offset, + ); + + $result = ['wide' => [], 'exact' => []]; + + $hasMore = false; + if (count($identifiers) > $limit) { + $hasMore = true; + array_pop($identifiers); + } + + foreach ($identifiers as $row) { + $item = $this->rowToSearchResultItem($row); + if (strtolower($row['identifier_value']) === strtolower($search) + || strtolower($row['display_name']) === strtolower($search) + ) { + $result['exact'][] = $item; + } else { + $result['wide'][] = $item; + } + } + + if (!count($identifiers) && !$this->canValidateMethod()) { + $result['exact'][] = [ + 'label' => $search, + 'shareWithDisplayNameUnique' => $search, + 'key' => self::$method, + 'value' => [ + 'shareWith' => $search, + 'shareType' => self::TYPE_SIGNER, + ], + ]; + } + + $type = new SearchResultType('signer'); + $searchResult->addResultSet($type, $result['wide'], $result['exact']); + + return $hasMore; + } + + private function canValidateMethod(): bool { + return in_array(self::$method, ['Email', 'Account'], true); + } + + private function rowToSearchResultItem(array $row): array { + $item = [ + 'label' => $row['display_name'], + 'shareWithDisplayNameUnique' => $row['identifier_value'], + 'key' => $row['identifier_key'], + 'value' => [ + 'shareWith' => $row['identifier_value'], + 'shareType' => self::TYPE_SIGNER, + ] + ]; + + return $item; + } +} diff --git a/lib/Controller/IdentifyAccountController.php b/lib/Controller/IdentifyAccountController.php index 07aab7a6ca..0592ce5c2d 100644 --- a/lib/Controller/IdentifyAccountController.php +++ b/lib/Controller/IdentifyAccountController.php @@ -9,6 +9,7 @@ namespace OCA\Libresign\Controller; use OCA\Libresign\AppInfo\Application; +use OCA\Libresign\Collaboration\Collaborators\SignerPlugin; use OCA\Libresign\Middleware\Attribute\RequireManager; use OCA\Libresign\ResponseDefinitions; use OCA\Libresign\Service\IdentifyMethod\Account; @@ -55,14 +56,17 @@ public function __construct( #[NoAdminRequired] #[RequireManager] #[ApiRoute(verb: 'GET', url: '/api/{apiVersion}/identify-account/search', requirements: ['apiVersion' => '(v1)'])] - public function search(string $search = '', int $page = 1, int $limit = 25): DataResponse { + public function search(string $search = '', string $method = '', int $page = 1, int $limit = 25): DataResponse { // only search for string larger than a minimum length if (strlen($search) < 1) { return new DataResponse(); } + $shareTypes = $this->getShareTypes(); $lookup = false; + $offset = $limit * ($page - 1); + $this->registerPlugin($method); [$result] = $this->collaboratorSearch->search($search, $shareTypes, $lookup, $limit, $offset); $result['exact'] = $this->unifyResult($result['exact']); $result = $this->unifyResult($result); @@ -75,6 +79,19 @@ public function search(string $search = '', int $page = 1, int $limit = 25): Dat return new DataResponse($return); } + private function registerPlugin(string $method): void { + SignerPlugin::setMethod($method); + + $refObject = new \ReflectionObject($this->collaboratorSearch); + $refProperty = $refObject->getProperty('pluginList'); + $refProperty->setAccessible(true); + + $plugins = $refProperty->getValue($this->collaboratorSearch); + $plugins[SignerPlugin::TYPE_SIGNER] = [SignerPlugin::class]; + + $refProperty->setValue($this->collaboratorSearch, $plugins); + } + private function getShareTypes(): array { if (count($this->shareTypes) > 0) { return $this->shareTypes; @@ -87,6 +104,8 @@ private function getShareTypes(): array { if ($settings['enabled']) { $this->shareTypes[] = IShare::TYPE_USER; } + + $this->shareTypes[] = SignerPlugin::TYPE_SIGNER; return $this->shareTypes; } @@ -106,21 +125,33 @@ private function unifyResult(array $list): array { } private function formatForNcSelect(array $list): array { + $return = []; foreach ($list as $key => $item) { - $list[$key] = [ + $return[$key] = [ 'id' => $item['value']['shareWith'], 'isNoUser' => $item['value']['shareType'] !== IShare::TYPE_USER, 'displayName' => $item['label'], 'subname' => $item['shareWithDisplayNameUnique'] ?? '', - 'shareType' => $item['value']['shareType'], ]; if ($item['value']['shareType'] === IShare::TYPE_EMAIL) { - $list[$key]['icon'] = 'icon-mail'; + $return[$key]['method'] = 'email'; + $return[$key]['icon'] = 'icon-mail'; } elseif ($item['value']['shareType'] === IShare::TYPE_USER) { - $list[$key]['icon'] = 'icon-user'; + $return[$key]['method'] = 'account'; + $return[$key]['icon'] = 'icon-user'; + } elseif ($item['value']['shareType'] === SignerPlugin::TYPE_SIGNER) { + $return[$key]['method'] = $item['key']; + if ($item['key'] === 'email') { + $return[$key]['icon'] = 'icon-mail'; + } elseif ($item['key'] === 'account') { + $return[$key]['icon'] = 'icon-user'; + } else { + $return[$key]['iconSvg'] = 'svg' . ucfirst($item['key']); + $return[$key]['iconName'] = $item['key']; + } } } - return $list; + return $return; } private function addHerselfAccount(array $return, string $search): array { diff --git a/lib/Db/IdentifyMethodMapper.php b/lib/Db/IdentifyMethodMapper.php index 10e7cfbce4..68707c4a39 100644 --- a/lib/Db/IdentifyMethodMapper.php +++ b/lib/Db/IdentifyMethodMapper.php @@ -73,4 +73,63 @@ public function neutralizeDeletedUser(string $userId, string $displayName): void $update->executeStatement(); } } + + public function searchByIdentifierValue(string $search, string $userId, string $method, int $limit = 20, int $offset = 0): array { + $qb = $this->db->getQueryBuilder(); + + $latestQb = $this->db->getQueryBuilder(); + $latestQb->select('im2.identifier_key') + ->addSelect('im2.identifier_value') + ->addSelect($latestQb->func()->max('sr2.created_at', 'created_at')) + ->from('libresign_identify_method', 'im2') + ->join('im2', 'libresign_sign_request', 'sr2', + $latestQb->expr()->eq('sr2.id', 'im2.sign_request_id') + ) + ->join('im2', 'libresign_file', 'f2', + $latestQb->expr()->eq('f2.id', 'sr2.file_id') + ) + ->where($latestQb->expr()->eq('f2.user_id', $latestQb->createNamedParameter($userId))); + if (!empty($method)) { + $latestQb->andWhere($latestQb->expr()->eq('im2.identifier_key', $latestQb->createNamedParameter($method))); + } + $latestQb->andWhere( + $latestQb->expr()->orX( + $latestQb->expr()->iLike( + 'im2.identifier_value', + $latestQb->createNamedParameter('%' . $this->db->escapeLikeParameter($search) . '%') + ), + $latestQb->expr()->iLike( + 'sr2.display_name', + $latestQb->createNamedParameter('%' . $this->db->escapeLikeParameter($search) . '%') + ) + ) + ) + ->groupBy('im2.identifier_key') + ->addGroupBy('im2.identifier_value'); + + foreach ($latestQb->getParameters() as $name => $value) { + $qb->setParameter($name, $value); + } + + $qb->select('im.identifier_key', 'im.identifier_value', 'sr.display_name') + ->from('libresign_identify_method', 'im') + ->join('im', $qb->createFunction('(' . $latestQb->getSQL() . ')'), 'latest', + $qb->expr()->andX( + $qb->expr()->eq('latest.identifier_key', 'im.identifier_key'), + $qb->expr()->eq('latest.identifier_value', 'im.identifier_value') + ) + ) + ->join('im', 'libresign_sign_request', 'sr', + $qb->expr()->eq('sr.id', 'im.sign_request_id'), + ) + ->setMaxResults($limit) + ->setFirstResult($offset); + + $cursor = $qb->executeQuery(); + $return = []; + while ($row = $cursor->fetch()) { + $return[] = $row; + } + return $return; + } } From 5a5f4d262348cf858164005a128160d92a3f9c22 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Mon, 22 Sep 2025 17:14:26 -0300 Subject: [PATCH 03/51] fix: add pendign param Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Controller/IdentifyAccountController.php | 1 + lib/Controller/SignFileController.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Controller/IdentifyAccountController.php b/lib/Controller/IdentifyAccountController.php index 0592ce5c2d..071a2b45d0 100644 --- a/lib/Controller/IdentifyAccountController.php +++ b/lib/Controller/IdentifyAccountController.php @@ -46,6 +46,7 @@ public function __construct( * Used to identify who can sign the document. The return of this endpoint is related with Administration Settiongs > LibreSign > Identify method. * * @param string $search search params + * @param string $method filter by method (email, account, sms, signal, telegram, whatsapp, xmpp) * @param int $page the number of page to return. Default: 1 * @param int $limit Total of elements to return. Default: 25 * @return DataResponse diff --git a/lib/Controller/SignFileController.php b/lib/Controller/SignFileController.php index 8bbff6170e..1579e8f089 100644 --- a/lib/Controller/SignFileController.php +++ b/lib/Controller/SignFileController.php @@ -220,7 +220,7 @@ public function signRenew(string $method): DataResponse { * * @param string $uuid UUID of LibreSign file * @param 'account'|'email'|null $identifyMethod Identify signer method - * @param string|null $signMethod Method used to sign the document, i.e. emailToken, account, clickToSign + * @param string|null $signMethod Method used to sign the document, i.e. emailToken, account, clickToSign, sms, signal, telegram, whatsapp, xmpp * @param string|null $identify Identify value, i.e. the signer email, account or phone number * @return DataResponse|DataResponse * @@ -246,7 +246,7 @@ public function getCodeUsingUuid(string $uuid, ?string $identifyMethod, ?string * * @param int $fileId Id of LibreSign file * @param 'account'|'email'|null $identifyMethod Identify signer method - * @param string|null $signMethod Method used to sign the document, i.e. emailToken, account, clickToSign + * @param string|null $signMethod Method used to sign the document, i.e. emailToken, account, clickToSign, sms, signal, telegram, whatsapp, xmpp * @param string|null $identify Identify value, i.e. the signer email, account or phone number * @return DataResponse|DataResponse * From 10898291601868ee9cb0ac8aaa07c0ed3aaa53e8 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Mon, 22 Sep 2025 17:15:19 -0300 Subject: [PATCH 04/51] chore: update openapi Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- openapi-full.json | 13 +++++++++++-- openapi.json | 13 +++++++++++-- src/types/openapi/openapi-full.ts | 6 ++++-- src/types/openapi/openapi.ts | 6 ++++-- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/openapi-full.json b/openapi-full.json index 47a493f511..421a6db16e 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -5508,6 +5508,15 @@ "default": "" } }, + { + "name": "method", + "in": "query", + "description": "filter by method (email, account, sms, signal, telegram, whatsapp, xmpp)", + "schema": { + "type": "string", + "default": "" + } + }, { "name": "page", "in": "query", @@ -7330,7 +7339,7 @@ "signMethod": { "type": "string", "nullable": true, - "description": "Method used to sign the document, i.e. emailToken, account, clickToSign" + "description": "Method used to sign the document, i.e. emailToken, account, clickToSign, sms, signal, telegram, whatsapp, xmpp" }, "identify": { "type": "string", @@ -7490,7 +7499,7 @@ "signMethod": { "type": "string", "nullable": true, - "description": "Method used to sign the document, i.e. emailToken, account, clickToSign" + "description": "Method used to sign the document, i.e. emailToken, account, clickToSign, sms, signal, telegram, whatsapp, xmpp" }, "identify": { "type": "string", diff --git a/openapi.json b/openapi.json index 7d000b01f8..432d8504a3 100644 --- a/openapi.json +++ b/openapi.json @@ -5361,6 +5361,15 @@ "default": "" } }, + { + "name": "method", + "in": "query", + "description": "filter by method (email, account, sms, signal, telegram, whatsapp, xmpp)", + "schema": { + "type": "string", + "default": "" + } + }, { "name": "page", "in": "query", @@ -7183,7 +7192,7 @@ "signMethod": { "type": "string", "nullable": true, - "description": "Method used to sign the document, i.e. emailToken, account, clickToSign" + "description": "Method used to sign the document, i.e. emailToken, account, clickToSign, sms, signal, telegram, whatsapp, xmpp" }, "identify": { "type": "string", @@ -7343,7 +7352,7 @@ "signMethod": { "type": "string", "nullable": true, - "description": "Method used to sign the document, i.e. emailToken, account, clickToSign" + "description": "Method used to sign the document, i.e. emailToken, account, clickToSign, sms, signal, telegram, whatsapp, xmpp" }, "identify": { "type": "string", diff --git a/src/types/openapi/openapi-full.ts b/src/types/openapi/openapi-full.ts index 262d421096..661335d7ed 100644 --- a/src/types/openapi/openapi-full.ts +++ b/src/types/openapi/openapi-full.ts @@ -3397,6 +3397,8 @@ export interface operations { query?: { /** @description search params */ search?: string; + /** @description filter by method (email, account, sms, signal, telegram, whatsapp, xmpp) */ + method?: string; /** @description the number of page to return. Default: 1 */ page?: number; /** @description Total of elements to return. Default: 25 */ @@ -4125,7 +4127,7 @@ export interface operations { * @enum {string|null} */ identifyMethod?: "account" | "email" | null; - /** @description Method used to sign the document, i.e. emailToken, account, clickToSign */ + /** @description Method used to sign the document, i.e. emailToken, account, clickToSign, sms, signal, telegram, whatsapp, xmpp */ signMethod?: string | null; /** @description Identify value, i.e. the signer email, account or phone number */ identify?: string | null; @@ -4189,7 +4191,7 @@ export interface operations { * @enum {string|null} */ identifyMethod?: "account" | "email" | null; - /** @description Method used to sign the document, i.e. emailToken, account, clickToSign */ + /** @description Method used to sign the document, i.e. emailToken, account, clickToSign, sms, signal, telegram, whatsapp, xmpp */ signMethod?: string | null; /** @description Identify value, i.e. the signer email, account or phone number */ identify?: string | null; diff --git a/src/types/openapi/openapi.ts b/src/types/openapi/openapi.ts index 5397b27750..c6e11dce6c 100644 --- a/src/types/openapi/openapi.ts +++ b/src/types/openapi/openapi.ts @@ -3073,6 +3073,8 @@ export interface operations { query?: { /** @description search params */ search?: string; + /** @description filter by method (email, account, sms, signal, telegram, whatsapp, xmpp) */ + method?: string; /** @description the number of page to return. Default: 1 */ page?: number; /** @description Total of elements to return. Default: 25 */ @@ -3801,7 +3803,7 @@ export interface operations { * @enum {string|null} */ identifyMethod?: "account" | "email" | null; - /** @description Method used to sign the document, i.e. emailToken, account, clickToSign */ + /** @description Method used to sign the document, i.e. emailToken, account, clickToSign, sms, signal, telegram, whatsapp, xmpp */ signMethod?: string | null; /** @description Identify value, i.e. the signer email, account or phone number */ identify?: string | null; @@ -3865,7 +3867,7 @@ export interface operations { * @enum {string|null} */ identifyMethod?: "account" | "email" | null; - /** @description Method used to sign the document, i.e. emailToken, account, clickToSign */ + /** @description Method used to sign the document, i.e. emailToken, account, clickToSign, sms, signal, telegram, whatsapp, xmpp */ signMethod?: string | null; /** @description Identify value, i.e. the signer email, account or phone number */ identify?: string | null; From a795fbf1a03969e5bbc15fb7edee7bd56b96fc6d Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Mon, 22 Sep 2025 17:17:40 -0300 Subject: [PATCH 05/51] fix: remove todo and fix method to use twofactor_gateway Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../SignatureMethod/TokenService.php | 49 ++++++++----------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php b/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php index c576ce6c91..6ae0b1f197 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php @@ -9,49 +9,42 @@ namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod; use OCA\Libresign\Service\MailService; +use OCP\AppFramework\OCS\OCSForbiddenException; +use OCP\IL10N; use OCP\Security\IHasher; use OCP\Security\ISecureRandom; +use OCP\Server; class TokenService { public const TOKEN_LENGTH = 6; - 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 ISecureRandom $secureRandom, private IHasher $hasher, private MailService $mail, + private IL10N $l10n, ) { } - /** - * @todo check this code and put to work - */ - public function sendCodeByGateway(string $code, string $gatewayName): void { - // $user = \OCP\Server::get(IUserSession::class)->getUser(); - // $gateway = $this->getGateway($user, $gatewayName); + public function sendCodeByGateway(string $identifier, string $code, string $gatewayName): string { + $gateway = $this->getGateway($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)); - // } + $code = $this->secureRandom->generate(self::TOKEN_LENGTH, ISecureRandom::CHAR_DIGITS); + $gateway->send($identifier, $this->l10n->t('%s is your LibreSign verification code.', $code)); + return $this->hasher->hash($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 + * @return \OCA\TwoFactorGateway\Provider\Gateway\IGateway + */ + private function getGateway(string $gatewayName) { + $factory = Server::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 28875646277a850c5b791911b6302481d2fee697 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Mon, 22 Sep 2025 17:21:31 -0300 Subject: [PATCH 06/51] chore: remove direct access to property and implement getter Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../IdentifyMethod/AbstractIdentifyMethod.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php index 1718776ade..77cb40fdac 100644 --- a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php @@ -18,6 +18,7 @@ use OCA\Libresign\Exception\LibresignException; use OCA\Libresign\Helper\JSActions; use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\AbstractSignatureMethod; +use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\ISignatureMethod; use OCA\Libresign\Service\SessionService; use OCA\Libresign\Vendor\Wobeto\EmailBlur\Blur; use OCP\IUser; @@ -34,7 +35,7 @@ abstract class AbstractIdentifyMethod implements IIdentifyMethod { * @var string[] */ public array $availableSignatureMethods = []; - protected string $defaultSignatureMethod = ''; + protected string $defaultSignatureMethod = ISignatureMethod::SIGNATURE_METHOD_CLICK_TO_SIGN; /** * @var AbstractSignatureMethod[] */ @@ -85,8 +86,12 @@ public function signatureMethodsToArray(): array { ], $this->signatureMethods); } + public function getAvailableSignatureMethods(): array { + return $this->availableSignatureMethods; + } + public function getEmptyInstanceOfSignatureMethodByName(string $name): AbstractSignatureMethod { - if (!in_array($name, $this->availableSignatureMethods)) { + if (!in_array($name, $this->getAvailableSignatureMethods())) { throw new InvalidArgumentException(sprintf('%s is not a valid signature method of identify method %s', $name, $this->getName())); } $className = 'OCA\Libresign\Service\IdentifyMethod\\SignatureMethod\\' . ucfirst($name); @@ -310,7 +315,8 @@ private function loadSavedSettings(): void { return $carry; }, []); $enabled = false; - foreach ($this->availableSignatureMethods as $signatureMethodName) { + $availableSignatureMethods = $this->getAvailableSignatureMethods(); + foreach ($availableSignatureMethods as $signatureMethodName) { $this->signatureMethods[$signatureMethodName] = $this->getEmptyInstanceOfSignatureMethodByName($signatureMethodName); if (isset($this->settings['signatureMethods'][$signatureMethodName]['enabled']) From 02c1d605abc34ce9bfbac360b3e0e5d20776331c Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Mon, 22 Sep 2025 17:22:10 -0300 Subject: [PATCH 07/51] fix: don't return not allowed settings Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Service/IdentifyMethod/AbstractIdentifyMethod.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php index 77cb40fdac..dcb6f91081 100644 --- a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php @@ -326,6 +326,13 @@ private function loadSavedSettings(): void { $enabled = true; } } + if (isset($this->settings['signatureMethods'])) { + foreach (array_keys($this->settings['signatureMethods']) as $signatureMethodName) { + if (!in_array($signatureMethodName, $availableSignatureMethods, true)) { + unset($this->settings['signatureMethods'][$signatureMethodName]); + } + } + } if (!$enabled && $this->defaultSignatureMethod) { $this->signatureMethods[$this->defaultSignatureMethod]->enable(); } From d3660d41ee30d4d5890fa365cb59a4c0cb96f1a3 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Mon, 22 Sep 2025 17:23:17 -0300 Subject: [PATCH 08/51] chore: add setter and getter to name Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Service/IdentifyMethod/AbstractIdentifyMethod.php | 8 ++++++-- lib/Service/IdentifyMethod/Account.php | 2 +- lib/Service/IdentifyMethod/Email.php | 2 +- lib/Service/IdentifyMethod/IIdentifyMethod.php | 1 + .../IdentifyMethod/SignatureMethod/ClickToSign.php | 2 +- lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php | 2 +- 6 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php index dcb6f91081..ed97bb50a3 100644 --- a/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/AbstractIdentifyMethod.php @@ -61,6 +61,10 @@ public function getFriendlyName(): string { return $this->friendlyName; } + public function setFriendlyName(string $friendlyName): void { + $this->friendlyName = $friendlyName; + } + public function setCodeSentByUser(string $code): void { $this->codeSentByUser = $code; } @@ -80,7 +84,7 @@ public function getEntity(): IdentifyMethod { public function signatureMethodsToArray(): array { return array_map(fn (AbstractSignatureMethod $method) => [ - 'label' => $method->friendlyName, + 'label' => $method->getFriendlyName(), 'name' => $method->getName(), 'enabled' => $method->isEnabled(), ], $this->signatureMethods); @@ -289,7 +293,7 @@ protected function getSettingsFromDatabase(array $default = [], array $immutable $default = array_merge( [ 'name' => $this->name, - 'friendly_name' => $this->friendlyName, + 'friendly_name' => $this->getFriendlyName(), 'enabled' => true, 'mandatory' => true, 'signatureMethods' => $this->signatureMethodsToArray(), diff --git a/lib/Service/IdentifyMethod/Account.php b/lib/Service/IdentifyMethod/Account.php index 0771b688e3..e9f8dcf577 100644 --- a/lib/Service/IdentifyMethod/Account.php +++ b/lib/Service/IdentifyMethod/Account.php @@ -47,7 +47,7 @@ public function __construct( private MailService $mail, ) { // TRANSLATORS Name of possible authenticator method. This signalize that the signer could be identified by Nextcloud acccount - $this->friendlyName = $this->identifyService->getL10n()->t('Account'); + $this->setFriendlyName($this->identifyService->getL10n()->t('Account')); parent::__construct( $identifyService, ); diff --git a/lib/Service/IdentifyMethod/Email.php b/lib/Service/IdentifyMethod/Email.php index 78e0cfdfea..30be6c60ed 100644 --- a/lib/Service/IdentifyMethod/Email.php +++ b/lib/Service/IdentifyMethod/Email.php @@ -37,7 +37,7 @@ public function __construct( private LoggerInterface $logger, ) { // TRANSLATORS Name of possible authenticator method. This signalize that the signer could be identified by email - $this->friendlyName = $this->identifyService->getL10n()->t('Email'); + $this->setFriendlyName($this->identifyService->getL10n()->t('Email')); parent::__construct( $identifyService, ); diff --git a/lib/Service/IdentifyMethod/IIdentifyMethod.php b/lib/Service/IdentifyMethod/IIdentifyMethod.php index a7d09ab297..e18e8d24c5 100644 --- a/lib/Service/IdentifyMethod/IIdentifyMethod.php +++ b/lib/Service/IdentifyMethod/IIdentifyMethod.php @@ -15,6 +15,7 @@ interface IIdentifyMethod { public static function getId(): string; public function getName(): string; + public function setFriendlyName(string $friendlyName): void; public function getFriendlyName(): string; public function setCodeSentByUser(string $code): void; public function cleanEntity(): void; diff --git a/lib/Service/IdentifyMethod/SignatureMethod/ClickToSign.php b/lib/Service/IdentifyMethod/SignatureMethod/ClickToSign.php index 8a34aecf12..bfcfd17b71 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/ClickToSign.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/ClickToSign.php @@ -15,7 +15,7 @@ public function __construct( protected IdentifyService $identifyService, ) { // TRANSLATORS Name of possible authenticator method. This signalize that the signer only need to click to sign after was identified - $this->friendlyName = $this->identifyService->getL10n()->t('Click to sign'); + $this->setFriendlyName($this->identifyService->getL10n()->t('Click to sign')); parent::__construct( $identifyService, ); diff --git a/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php index 2aa91ec597..8a43a667de 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/EmailToken.php @@ -19,7 +19,7 @@ public function __construct( protected TokenService $tokenService, ) { // TRANSLATORS Name of possible authenticator method. This signalize that the signer could be identified by email - $this->friendlyName = $this->identifyService->getL10n()->t('Email token'); + $this->setFriendlyName($this->identifyService->getL10n()->t('Email token')); parent::__construct( $identifyService, ); From 9858fe5842e165d9f9a814371c50767d1e90eff1 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Mon, 22 Sep 2025 17:24:14 -0300 Subject: [PATCH 09/51] fix: make more generic Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Service/SignFileService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Service/SignFileService.php b/lib/Service/SignFileService.php index 91a11b5d58..23ab1b0b92 100644 --- a/lib/Service/SignFileService.php +++ b/lib/Service/SignFileService.php @@ -37,7 +37,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 OCA\Libresign\Service\IdentifyMethod\SignatureMethod\IToken; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\AppFramework\Utility\ITimeFactory; @@ -611,7 +611,7 @@ public function requestCode( } catch (InvalidArgumentException) { continue; } - /** @var EmailToken $signatureMethod */ + /** @var IToken $signatureMethod */ $signatureMethod->requestCode($identify); return; } From 260c0574c3d0666476432a8b3d2000e373cbee24 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Mon, 22 Sep 2025 17:24:59 -0300 Subject: [PATCH 10/51] feat: change way to add a signer, use a modal to be possible split Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- src/Components/Request/IdentifySigner.vue | 156 +++++------------- .../{AccountOrEmail.vue => SignerSelect.vue} | 57 ++++--- .../RightSidebar/RequestSignatureTab.vue | 84 +++++++++- 3 files changed, 154 insertions(+), 143 deletions(-) rename src/Components/Request/{AccountOrEmail.vue => SignerSelect.vue} (76%) diff --git a/src/Components/Request/IdentifySigner.vue b/src/Components/Request/IdentifySigner.vue index bc0ac829e3..17e66729f4 100644 --- a/src/Components/Request/IdentifySigner.vue +++ b/src/Components/Request/IdentifySigner.vue @@ -4,13 +4,12 @@ -->