diff --git a/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php b/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php index 98bf1f1658..91f901f612 100644 --- a/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php +++ b/lib/Service/IdentifyMethod/SignatureMethod/TokenService.php @@ -47,8 +47,8 @@ private function getGateway(string $gatewayName) { throw new LibresignException('App Two-Factor Gateway is not installed.'); } - $gateway = $factory->getGateway($gatewayName); - if (!$gateway->getConfig()->isComplete()) { + $gateway = $factory->get($gatewayName); + if (!$gateway->isComplete()) { throw new OCSForbiddenException($this->l10n->t('Gateway %s not configured on Two-Factor Gateway.', $gatewayName)); } return $gateway; diff --git a/lib/Service/SignFileService.php b/lib/Service/SignFileService.php index 15657609bc..b9b5f59e89 100644 --- a/lib/Service/SignFileService.php +++ b/lib/Service/SignFileService.php @@ -612,7 +612,8 @@ public function requestCode( continue; } /** @var IToken $signatureMethod */ - $signatureMethod->requestCode($identify, $identifyMethod->getEntity()->getIdentifierKey()); + $identifier = $identify ?: $identifyMethod->getEntity()->getIdentifierValue(); + $signatureMethod->requestCode($identifier, $identifyMethod->getEntity()->getIdentifierKey()); return; } throw new LibresignException($this->l10n->t('Sending authorization code not enabled.')); diff --git a/src/store/signMethods.js b/src/store/signMethods.js index 979c745aff..18b2694bb9 100644 --- a/src/store/signMethods.js +++ b/src/store/signMethods.js @@ -77,6 +77,12 @@ export const useSignMethodsStore = defineStore('signMethods', { return Object.hasOwn(this.settings, 'sms') && this.settings.sms.needCode }, + needTokenCode() { + const tokenMethods = ['sms', 'whatsapp', 'signal', 'telegram', 'xmpp'] + return tokenMethods.some(method => + Object.hasOwn(this.settings, method) && this.settings[method].needCode + ) + }, needCertificate() { return this.certificateEngine === 'none' && !this.hasSignatureFile() }, diff --git a/src/views/SignPDF/_partials/ModalSMSManager.vue b/src/views/SignPDF/_partials/ModalTokenManager.vue similarity index 61% rename from src/views/SignPDF/_partials/ModalSMSManager.vue rename to src/views/SignPDF/_partials/ModalTokenManager.vue index 3c5ecf7689..6bc2e435fe 100644 --- a/src/views/SignPDF/_partials/ModalSMSManager.vue +++ b/src/views/SignPDF/_partials/ModalTokenManager.vue @@ -4,15 +4,14 @@ --> @@ -61,6 +54,7 @@ import NcTextField from '@nextcloud/vue/components/NcTextField' import { settingsService } from '../../../domains/settings/index.js' import { useSignStore } from '../../../store/sign.js' +import { useSignMethodsStore } from '../../../store/signMethods.js' const sanitizeNumber = val => { val = val.replace(/\D/g, '') @@ -68,7 +62,7 @@ const sanitizeNumber = val => { } export default { - name: 'ModalSMSManager', + name: 'ModalTokenManager', components: { NcDialog, NcLoadingIcon, @@ -78,19 +72,34 @@ export default { props: { phoneNumber: { type: String, - required: true, + required: false, + default: '', }, }, setup() { const signStore = useSignStore() - return { signStore } + const signMethodsStore = useSignMethodsStore() + return { signStore, signMethodsStore } + }, + data() { + return { + token: '', + newPhoneNumber: this.phoneNumber || '', + tokenRequested: false, + loading: false, + } + }, + computed: { + activeTokenMethod() { + const tokenMethods = ['sms', 'whatsapp', 'signal', 'telegram', 'xmpp'] + return tokenMethods.find(method => + Object.hasOwn(this.signMethodsStore.settings, method) + ) || 'sms' + }, + activeIdentifyMethod() { + return this.activeTokenMethod + }, }, - data: () => ({ - token: '', - newPhoneNumber: this.phoneNumber, - tokenRequested: false, - loading: false, - }), methods: { async saveNumber() { this.loading = true @@ -123,22 +132,40 @@ export default { await this.$nextTick() try { + const params = { + identifyMethod: this.activeIdentifyMethod, + signMethod: this.activeTokenMethod, + } + if (this.signStore.document.fileId) { - const { data } = await axios.post(generateOcsUrl('/apps/libresign/api/v1/sign/file_id/{fileId}/code', { - fileId: this.signStore.document.fileId, - })) + const { data } = await axios.post( + generateOcsUrl('/apps/libresign/api/v1/sign/file_id/{fileId}/code', { + fileId: this.signStore.document.fileId, + }), + params + ) showSuccess(data.ocs.data.message) } else { - const signer = this.signStore.document.signers.find(row => row.me) || {} - const { data } = await axios.post(generateOcsUrl('/apps/libresign/api/v1/sign/uuid/{fileId}/code', { + const signer = this.signStore.document.signers.find(row => row.me) || {} + const { data } = await axios.post( + generateOcsUrl('/apps/libresign/api/v1/sign/uuid/{uuid}/code', { uuid: signer.sign_uuid, - })) + }), + params + ) showSuccess(data.ocs.data.message) } - this.tokenRequested = true - } catch (err) { - showError(err.response.data.ocs.data.message) - } finally { + this.tokenRequested = true + } catch (err) { + const errorMessage = err.response?.data?.ocs?.data?.message || err.response?.data?.message || err.message + + if (errorMessage && errorMessage.includes('Invalid configuration')) { + const method = this.activeTokenMethod.charAt(0).toUpperCase() + this.activeTokenMethod.slice(1) + showError(t('libresign', '{method} is not configured. Please contact your administrator.', { method })) + } else { + showError(errorMessage) + } + } finally { this.loading = false } }, diff --git a/src/views/SignPDF/_partials/Sign.vue b/src/views/SignPDF/_partials/Sign.vue index 3b91f01c38..572bf622c8 100644 --- a/src/views/SignPDF/_partials/Sign.vue +++ b/src/views/SignPDF/_partials/Sign.vue @@ -130,9 +130,9 @@ :useModal="true" :errors="errors" @certificate:uploaded="onSignatureFileCreated" /> - + Object.hasOwn(this.signMethodsStore.settings, method) + ) || 'sms' + await this.signDocument({ - method: 'sms', + method: activeMethod, token, }) }, @@ -395,7 +400,7 @@ export default { this.showModalAndResetErrors('createSignature') return } - if (this.signMethodsStore.needSmsCode()) { + if (this.signMethodsStore.needTokenCode()) { this.showModalAndResetErrors('sms') return }