Skip to content

Commit

Permalink
Start to implement jeidison/pdf-signe
Browse files Browse the repository at this point in the history
Signed-off-by: Vitor Mattos <vitor@php.rio>
  • Loading branch information
vitormattos committed Apr 23, 2024
1 parent 992410c commit 6e913ec
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 2 deletions.
144 changes: 144 additions & 0 deletions lib/Handler/PhpNativeHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2023 Vitor Mattos <vitor@php.rio>
*
* @author Vitor Mattos <vitor@php.rio>
*
* @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 <http://www.gnu.org/licenses/>.
*/

namespace OCA\Libresign\Handler;

use Jeidison\JSignPDF\JSignPDF;
use Jeidison\JSignPDF\Sign\JSignParam;
use OCA\Libresign\Exception\LibresignException;
use OCP\AppFramework\Services\IAppConfig;
use Psr\Log\LoggerInterface;

class PhpNativeHandler extends SignEngineHandler {
/** @var JSignPDF */
private $jSignPdf;
/** @var JSignParam */
private $jSignParam;
public const VERSION = '2.2.2';

public function __construct(
private IAppConfig $appConfig,
private LoggerInterface $logger,
) {
}

public function setJSignPdf(JSignPDF $jSignPdf): void {
$this->jSignPdf = $jSignPdf;
}

public function getJSignPdf(): JSignPDF {
if (!$this->jSignPdf) {
// @codeCoverageIgnoreStart
$this->setJSignPdf(new JSignPDF());
// @codeCoverageIgnoreEnd
}
return $this->jSignPdf;
}

/**
* @psalm-suppress MixedReturnStatement
*/
public function getJSignParam(): JSignParam {
if (!$this->jSignParam) {
$javaPath = $this->appConfig->getAppValue('java_path');
$this->jSignParam = (new JSignParam())
->setTempPath(
$this->appConfig->getAppValue('jsignpdf_temp_path', sys_get_temp_dir() . DIRECTORY_SEPARATOR)
)
->setIsUseJavaInstalled(empty($javaPath))
->setjSignPdfJarPath(
$this->appConfig->getAppValue('jsignpdf_jar_path', '/opt/jsignpdf-' . self::VERSION . '/JSignPdf.jar')
);
if (!empty($javaPath)) {
if (!file_exists($javaPath)) {
throw new \Exception('Invalid Java binary. Run occ libresign:install --java');
}
$this->jSignParam->setJavaPath($javaPath);
}
}
return $this->jSignParam;
}

/**
* @psalm-suppress MixedReturnStatement
*/
public function sign(): string {
$param = $this->getJSignParam()
->setCertificate($this->getCertificate())
->setPdf($this->getInputFile()->getContent())
->setPassword($this->getPassword());

$signed = $this->signUsingVisibleElements();
if ($signed) {
return $signed;
}
$jSignPdf = $this->getJSignPdf();
$jSignPdf->setParam($param);
return $this->signWrapper($jSignPdf);
}

private function signUsingVisibleElements(): string {
$visibleElements = $this->getvisibleElements();
if ($visibleElements) {
$jSignPdf = $this->getJSignPdf();
$param = $this->getJSignParam();
foreach ($visibleElements as $element) {
$param
->setJSignParameters(
$param->getJSignParameters() .
' -pg ' . $element->getFileElement()->getPage() .
' -llx ' . $element->getFileElement()->getLlx() .
' -lly ' . $element->getFileElement()->getLly() .
' -urx ' . $element->getFileElement()->getUrx() .
' -ury ' . $element->getFileElement()->getUry() .
' --l2-text ""' .
' -V' .
' --bg-path ' . $element->getTempFile()
);
$jSignPdf->setParam($param);
$signed = $this->signWrapper($jSignPdf);
}
return $signed;
}
return '';
}

private function signWrapper(JSignPDF $jSignPDF): string {
try {
return $jSignPDF->sign();
} catch (\Throwable $th) {
$rows = str_getcsv($th->getMessage());
$hashAlgorithm = array_filter($rows, fn ($r) => str_contains($r, 'The chosen hash algorithm'));
if (!empty($hashAlgorithm)) {
$hashAlgorithm = current($hashAlgorithm);
$hashAlgorithm = trim($hashAlgorithm, 'INFO ');
$hashAlgorithm = str_replace('\"', '"', $hashAlgorithm);
$hashAlgorithm = preg_replace('/\.( )/', ".\n", $hashAlgorithm);
throw new LibresignException($hashAlgorithm);
}
$this->logger->error('Error at JSignPdf side. LibreSign can not do nothing. Follow the error message: ' . $th->getMessage());
throw new \Exception($th->getMessage());
}
}
}
4 changes: 2 additions & 2 deletions lib/Handler/Pkcs12Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ public function getPfx(?string $uid = null): string {
}

private function getHandler(): SignEngineHandler {
$sign_engine = $this->appConfig->getAppValue('sign_engine', 'JSignPdf');
$property = lcfirst($sign_engine) . 'Handler';
$signature_engine = $this->appConfig->getAppValue('signature_engine', 'JSignPdf');
$property = lcfirst($signature_engine) . 'Handler';
if (!property_exists($this, $property)) {
throw new LibresignException($this->l10n->t('Invalid Sign engine.'), 400);
}
Expand Down
8 changes: 8 additions & 0 deletions lib/Service/Install/ConfigureCheckService.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ public function checkSign(): array {
* @return ConfigureCheckHelper[]
*/
public function checkJSignPdf(): array {
$signatureEngine = $this->appConfig->getAppValue('signature_engine', 'jsignpdf');
if ($signatureEngine !== 'jsignpdf') {
return [];
}
$jsignpdJarPath = $this->appConfig->getAppValue('jsignpdf_jar_path');
if ($jsignpdJarPath) {
if (file_exists($jsignpdJarPath)) {
Expand Down Expand Up @@ -182,6 +186,10 @@ public function checkPdftk(): array {
* @return ConfigureCheckHelper[]
*/
private function checkJava(): array {
$signatureEngine = $this->appConfig->getAppValue('signature_engine', 'jsignpdf');
if ($signatureEngine !== 'jsignpdf') {
return [];
}
$javaPath = $this->appConfig->getAppValue('java_path');
if ($javaPath) {
if (file_exists($javaPath)) {
Expand Down
8 changes: 8 additions & 0 deletions lib/Service/Install/InstallService.php
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ public function setResource(string $resource): self {
}

public function installJava(?bool $async = false): void {
$signatureEngine = $this->appConfig->getAppValue('signature_engine', 'jsignpdf');
if ($signatureEngine !== 'jsignpdf') {
return [];
}
$this->setResource('java');
if ($async) {
$this->runAsync();
Expand Down Expand Up @@ -394,6 +398,10 @@ public function uninstallJava(): void {
}

public function installJSignPdf(?bool $async = false): void {
$signatureEngine = $this->appConfig->getAppValue('signature_engine', 'jsignpdf');
if ($signatureEngine !== 'jsignpdf') {
return [];
}
if (!extension_loaded('zip')) {
throw new RuntimeException('Zip extension is not available');
}
Expand Down
3 changes: 3 additions & 0 deletions src/views/Settings/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<template>
<NcSettingsSection :name="name">
<CertificateEngine />
<SignatureEngine />
<DownloadBinaries />
<ConfigureCheck />
<RootCertificateCfssl />
Expand All @@ -42,6 +43,7 @@
<script>
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
import CertificateEngine from './CertificateEngine.vue'
import SignatureEngine from './SignatureEngine.vue'
import DownloadBinaries from './DownloadBinaries.vue'
import ConfigureCheck from './ConfigureCheck.vue'
import RootCertificateCfssl from './RootCertificateCfssl.vue'
Expand All @@ -60,6 +62,7 @@ export default {
components: {
NcSettingsSection,
CertificateEngine,
SignatureEngine,
DownloadBinaries,
ConfigureCheck,
RootCertificateCfssl,
Expand Down
64 changes: 64 additions & 0 deletions src/views/Settings/SignatureEngine.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<template>
<NcSettingsSection :name="name" :description="description">
<div class="signature-engine-content">
<NcSelect input-id="signatureEngine"
:aria-label-combobox="description"
:clearable="false"
:value="value"
:options="options"
@input="saveEngine" />
</div>
</NcSettingsSection>
</template>
<script>
import { translate as t } from '@nextcloud/l10n'
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
import { emit } from '@nextcloud/event-bus'
import { loadState } from '@nextcloud/initial-state'
export default {
name: 'SignatureEngine',
components: {
NcSettingsSection,
NcSelect,
},
data() {
return {
name: t('libresign', 'Signature engine'),
description: t('libresign', 'Select the signature engine to sign the documents'),
value: [],
options: [
{ id: 'JSignPdf', label: 'JSignPdf' },
{ id: 'PhpNative', label: 'PHP native' },
],
}
},
beforeMount() {
const currentOption = {}
currentOption.id = loadState('libresign', 'signature_engine', 'JSignPdf')
if (currentOption.id === 'JSignPdf') {
currentOption.label = 'JSignPdf'
} else {
currentOption.label = 'PHP native'
}
this.value = [currentOption]
},
methods: {
saveEngine(selected) {
this.value = selected
OCP.AppConfig.setValue('libresign', 'signature_engine', selected.id, {
success() {
emit('libresign:signature-engine:changed', selected.id)
},
})
},
},
}
</script>
<style scoped>
.signature-engine-content{
display: flex;
flex-direction: column;
}
</style>

0 comments on commit 6e913ec

Please sign in to comment.