Create install commands

This commit is contained in:
Vitor Mattos 2022-01-30 11:44:50 -03:00 committed by backportbot[bot]
parent 3e70e6e103
commit b72ce4df20
12 changed files with 569 additions and 15 deletions

View file

@ -2,18 +2,37 @@
[![Coverage Status](https://coveralls.io/repos/github/LibreSign/libresign/badge.svg?branch=main)](https://coveralls.io/github/LibreSign/libresign?branch=main)
[![Start contributing](https://img.shields.io/github/issues/LibreSign/libresign/good%20first%20issue?color=7057ff&label=Contribute)](https://github.com/LibreSign/libresign/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22)
# LibreSign
Nextcloud app to sign PDF documents.
At the moment file signature requests must be requested via webhook. Read the documentation for more information.
<img src="img/LibreSign.png" />
**Table of contents**
- [Setup](#setup)
- [Java and JSignPDF](#java-and-jsignpdf)
- [Standalone](#standalone)
- [Using Docker](#using-docker)
- [CFSSL](#cfssl)
- [CFSS server handmade install](#cfss-server-handmade-install)
- [With docker-compose](#with-docker-compose)
- [Admin settings](#admin-settings)
- [Validation page](#validation-page)
- [Integrations](#integrations)
- [Full documentation](#full-documentation)
- [Contributing](#contributing)
## Setup
### Java and JSignPDF
#### Standalone
Run commands:
```bash
occ libresign:install:java
occ libresign:install:jsignpdf
```
#### Using Docker
Add the follow to Nextcloud PHP container Dockerfile
```Dockerfile
@ -21,12 +40,13 @@ Add the follow to Nextcloud PHP container Dockerfile
RUN apt-get update # Only include this line if necessary
RUN mkdir -p /usr/share/man/man1
RUN apt-get install -y default-jre unzip
RUN curl -OL https://sourceforge.net/projects/jsignpdf/files/stable/JSignPdf%202.0.0/jsignpdf-2.0.0.zip \
&& unzip jsignpdf-2.0.0.zip -d /opt \
&& rm jsignpdf-2.0.0.zip
RUN curl -OL https://sourceforge.net/projects/jsignpdf/files/stable/JSignPdf%201.6.5/JSignPdf-1.6.5.zip \
&& unzip JSignPdf-1.6.5.zip -d /opt \
&& rm JSignPdf-1.6.5.zip
```
### With CFSS server
### CFSSL
#### CFSS server handmade install
Don't is necessary if you use a docker setup
@ -36,7 +56,9 @@ https://github.com/cloudflare/cfssl
The URL of server you will use in [Admin settings](#admin-settings)
### With docker-compose
> **PS**: Use latest version, on many cases the version of package manage of linux distro is outdated and incompatible with LibreSign
#### With docker-compose
* Create a folder named cfssl in the same folder as your `docker-compose.yml` file. This folder will be used on one volume of the cfssl service.
* put the file [`/cfssl/entrypoint.sh`](https://github.com/LibreSign/libresign/blob/main/cfssl/entrypoint.sh) in `cfssl` folder
* Add the volume `./cfssl:/cfssl` in Nextcloud php service

View file

@ -33,6 +33,12 @@
<command>java</command>
<nextcloud min-version="21" max-version="21"/>
</dependencies>
<commands>
<command>OCA\Libresign\Command\Configure\Cfssl</command>
<command>OCA\Libresign\Command\Install\Cfssl</command>
<command>OCA\Libresign\Command\Install\Java</command>
<command>OCA\Libresign\Command\Install\JSignPdf</command>
</commands>
<settings>
<admin>OCA\Libresign\Settings\Admin</admin>
<admin-section>OCA\Libresign\Settings\AdminSettings</admin-section>

View file

@ -4,7 +4,7 @@
"type": "project",
"license": "AGPL",
"require": {
"jsignpdf/jsignpdf-php": "^1.0",
"jsignpdf/jsignpdf-php": "^1.1",
"endroid/qr-code": "^4.4",
"pagerfanta/pagerfanta": "^3.5",
"tecnickcom/tcpdf": "^6.4",

2
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "99fd89527bf457eec8b7d4ff3113f39f",
"content-hash": "ed3ad24b708cb070d5ef4232f972e100",
"packages": [
{
"name": "bacon/bacon-qr-code",

206
lib/Command/Base.php Normal file
View file

@ -0,0 +1,206 @@
<?php
declare(strict_types=1);
namespace OCA\Libresign\Command;
use OC\Archive\TAR;
use OC\Archive\ZIP;
use OC\Core\Command\Base as CommandBase;
use OC\SystemConfig;
use OCA\Libresign\AppInfo\Application;
use OCA\Libresign\Handler\JSignPdfHandler;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\ITempManager;
use RuntimeException;
class Base extends CommandBase {
/** @var ITempManager */
private $tempManager;
/** @var IClientService */
private $clientService;
/** @var IConfig */
protected $config;
/** @var SystemConfig */
private $systemConfig;
/** @var IRootFolder */
private $rootFolder;
public function __construct(
ITempManager $tempManager,
IClientService $clientService,
IConfig $config,
SystemConfig $systemConfig,
IRootFolder $rootFolder
) {
parent::__construct();
$this->tempManager = $tempManager;
$this->clientService = $clientService;
$this->config = $config;
$this->systemConfig = $systemConfig;
$this->rootFolder = $rootFolder;
}
protected function getFolder($path = ''): Folder {
$rootFolder = $this->getAppRootFolder();
try {
$folder = $rootFolder->newFolder(Application::APP_ID . DIRECTORY_SEPARATOR . $path);
} catch (\Throwable $th) {
$folder = $rootFolder->get(Application::APP_ID . DIRECTORY_SEPARATOR . $path);
}
return $folder;
}
private function getAppDataFolderName(): string {
$instanceId = $this->systemConfig->getValue('instanceid', null);
if ($instanceId === null) {
throw new \RuntimeException('no instance id!');
}
return 'appdata_' . $instanceId;
}
private function getDataDir(): string {
$dataDir = $this->systemConfig->getValue('datadirectory', \OC::$SERVERROOT . '/data/');
return $dataDir;
}
private function getAppRootFolder(): Folder {
$path = $this->getAppDataFolderName();
return $this->rootFolder->get($path);
}
protected function getFullPath(): string {
$folder = $this->getFolder();
return $this->getDataDir() . '/' . $folder->getInternalPath();
}
protected function installJava(): void {
$extractDir = $this->getFullPath();
if (PHP_OS_FAMILY === 'Windows') {
$url = 'https://download.java.net/openjdk/jdk8u41/ri/openjdk-8u41-b04-windows-i586-14_jan_2020.zip';
$tempFile = $this->tempManager->getTemporaryFile('.zip');
$executableExtension = '.exe';
$class = ZIP::class;
} else {
$url = 'https://download.java.net/openjdk/jdk8u41/ri/openjdk-8u41-b04-linux-x64-14_jan_2020.tar.gz';
$tempFile = $this->tempManager->getTemporaryFile('.tar.gz');
$executableExtension = '';
$class = TAR::class;
}
$client = $this->clientService->newClient();
$client->get($url, ['sink' => $tempFile, 'timeout' => 0]);
$extractor = new $class($tempFile);
$extractor->extract($extractDir);
$this->config->setAppValue(Application::APP_ID, 'java_path', $extractDir . '/java-se-8u41-ri/bin/java' . $executableExtension);
}
protected function uninstallJava(): void {
$javaPath = $this->config->getAppValue(Application::APP_ID, 'java_path');
if (!$javaPath) {
return;
}
$appFolder = $this->getAppRootFolder();
$name = $appFolder->getName();
// Remove prefix
$path = explode($name, $javaPath)[1];
// Remove binary path
$path = explode(DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR, $path)[0];
try {
$folder = $appFolder->get($path);
$folder->delete();
} catch (NotFoundException $e) {
}
$this->config->deleteAppValue(Application::APP_ID, 'java_path');
}
protected function installJSignPdf(): void {
if (!extension_loaded('zip')){
throw new RuntimeException('Zip extension is not available');
}
$extractDir = $this->getFullPath();
$tempFile = $this->tempManager->getTemporaryFile('.zip');
$url = 'https://sourceforge.net/projects/jsignpdf/files/stable/JSignPdf%20' . JSignPdfHandler::VERSION . '/jsignpdf-' . JSignPdfHandler::VERSION . '.zip';
$client = $this->clientService->newClient();
$client->get($url, ['sink' => $tempFile, 'timeout' => 0]);
$zip = new ZIP($tempFile);
$zip->extract($extractDir);
$fullPath = $extractDir . DIRECTORY_SEPARATOR. 'jsignpdf-' . JSignPdfHandler::VERSION . DIRECTORY_SEPARATOR. 'JSignPdf.jar';
$this->config->setAppValue(Application::APP_ID, 'jsignpdf_jar_path', $fullPath);
}
protected function uninstallJSignPdf(): void {
$jsignpdJarPath = $this->config->getAppValue(Application::APP_ID, 'jsignpdf_jar_path');
if (!$jsignpdJarPath) {
return;
}
$appFolder = $this->getAppRootFolder();
$name = $appFolder->getName();
// Remove prefix
$path = explode($name, $jsignpdJarPath)[1];
// Remove sufix
$path = trim($path, DIRECTORY_SEPARATOR . 'JSignPdf.jar');
try {
$folder = $appFolder->get($path);
$folder->delete();
} catch (NotFoundException $e) {
}
$this->config->deleteAppValue(Application::APP_ID, 'jsignpdf_jar_path');
}
protected function installCfssl(): void {
$folder = $this->getFolder();
$binName = 'cfssl';
if (PHP_OS_FAMILY === 'Windows') {
$url = 'https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssl_1.6.1_windows_amd64.exe';
$binName = 'cfssl.exe';
} elseif (PHP_OS_FAMILY === 'Darwin') {
$url = 'https://github.com/cloudflare/cfssl/releases/download/v1.6.1/multirootca_1.6.1_darwin_amd64';
} else {
$url = 'https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssl_1.6.1_linux_amd64';
}
$file = $folder->newFile($binName);
$fullPath = $this->getDataDir() . DIRECTORY_SEPARATOR . $file->getInternalPath();
$client = $this->clientService->newClient();
$client->get($url, ['sink' => $fullPath, 'timeout' => 0]);
if (PHP_OS_FAMILY !== 'Windows') {
chmod($fullPath, 0700);
}
$this->config->setAppValue(Application::APP_ID, 'cfssl_bin', 1);
}
protected function uninstallCfssl(): void {
$cfsslPath = $this->config->getAppValue(Application::APP_ID, 'cfssl_bin');
if (!$cfsslPath) {
return;
}
$appFolder = $this->getAppRootFolder();
$name = $appFolder->getName();
// Remove prefix
$path = explode($name, $cfsslPath)[1];
try {
$folder = $appFolder->get($path);
$folder->delete();
} catch (NotFoundException $e) {
}
$this->config->deleteAppValue(Application::APP_ID, 'cfssl_bin');
}
}

View file

@ -0,0 +1,127 @@
<?php
declare(strict_types=1);
namespace OCA\Libresign\Command\Configure;
use InvalidArgumentException;
use OC\SystemConfig;
use OCA\Libresign\AppInfo\Application;
use OCA\Libresign\Command\Base;
use OCA\Libresign\Service\AdminSignatureService;
use OCP\Files\IRootFolder;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\ITempManager;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class Cfssl extends Base {
/** @var AdminSignatureService */
private $adminSignatureService;
public function __construct(
ITempManager $tempManager,
IClientService $clientService,
IConfig $config,
SystemConfig $systemConfig,
IRootFolder $rootFolder,
AdminSignatureService $adminSignatureService
) {
parent::__construct(
$tempManager,
$clientService,
$config,
$systemConfig,
$rootFolder
);
$this->adminSignatureService = $adminSignatureService;
}
protected function configure(): void {
$this
->setName('libresign:configure:cfssl')
->setDescription('Configure Cfssl')
->addOption(
'cn',
null,
InputOption::VALUE_REQUIRED,
'Comon name'
)
->addOption(
'ou',
null,
InputOption::VALUE_REQUIRED,
'Organization unit'
)
->addOption(
'o',
'o',
InputOption::VALUE_REQUIRED,
'Organization'
)
->addOption(
'c',
'c',
InputOption::VALUE_REQUIRED,
'Country name'
)
->addOption(
'config-path',
null,
InputOption::VALUE_REQUIRED,
'Config path'
)
->addOption(
'cfssl-uri',
null,
InputOption::VALUE_REQUIRED,
'CFSSL URI'
);
}
protected function execute(InputInterface $input, OutputInterface $output): int {
if (!$commonName = $input->getOption('cn')) {
throw new InvalidArgumentException('Invalid Comon Name');
}
if (!$organizationUnit = $input->getOption('ou')) {
throw new InvalidArgumentException('Invalid Organization Unit');
}
if (!$organization = $input->getOption('o')) {
throw new InvalidArgumentException('Invalid Organization');
}
if (!$country = $input->getOption('c')) {
throw new InvalidArgumentException('Invalid Country');
}
if ($binary = $this->config->getAppValue(Application::APP_ID, 'cfssl_bin')) {
if ($input->getOption('config-path')) {
throw new InvalidArgumentException('Config path is not necessary');
}
if ($input->getOption('cfssl-uri')) {
throw new InvalidArgumentException('CFSSL URI is not necessary');
}
// create if not exist
$this->getFolder('cfssl_config');
$configPath = $this->getFullPath() . DIRECTORY_SEPARATOR . 'cfssl_config' . DIRECTORY_SEPARATOR;
$cfsslUri = 'http://127.0.0.1:8888/api/v1/cfssl/';
} else {
$output->writeln('CFSSL binary not found!');
if (!$configPath = $input->getOption('config-path')) {
throw new InvalidArgumentException('Invalid config path');
}
if (!$cfsslUri = $input->getOption('cfssl-uri')) {
throw new InvalidArgumentException('Invalid CFSSL API URI');
}
}
$this->adminSignatureService->generate(
$commonName,
$country,
$organization,
$organizationUnit,
$cfsslUri,
$configPath,
$binary
);
return static::SUCCESS;
}
}

View file

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace OCA\Libresign\Command\Install;
use OC\SystemConfig;
use OCA\Libresign\Command\Base;
use OCP\Files\IRootFolder;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\ITempManager;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class Cfssl extends Base {
public function __construct(
ITempManager $tempManager,
IClientService $clientService,
IConfig $config,
SystemConfig $systemConfig,
IRootFolder $rootFolder
) {
parent::__construct(
$tempManager,
$clientService,
$config,
$systemConfig,
$rootFolder
);
}
protected function configure(): void {
$this
->setName('libresign:install:cfssl')
->setDescription('Download CFSSL')
->addOption('uninstall',
null,
InputOption::VALUE_NONE,
'Uninstall standalone CFSSL'
);
}
protected function execute(InputInterface $input, OutputInterface $output): int {
if ($input->getOption('uninstall')) {
$this->uninstallCfssl();
} else {
$this->installCfssl();
}
return static::SUCCESS;
}
}

View file

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace OCA\Libresign\Command\Install;
use OC\SystemConfig;
use OCA\Libresign\Command\Base;
use OCP\Files\IRootFolder;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\ITempManager;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class JSignPdf extends Base {
public function __construct(
ITempManager $tempManager,
IClientService $clientService,
IConfig $config,
SystemConfig $systemConfig,
IRootFolder $rootFolder
) {
parent::__construct(
$tempManager,
$clientService,
$config,
$systemConfig,
$rootFolder
);
}
protected function configure(): void {
$this
->setName('libresign:install:jsignpdf')
->setDescription('Download and configure JSignPDF')
->addOption('uninstall',
null,
InputOption::VALUE_NONE,
'Uninstall standalone JSignPDF'
);
}
protected function execute(InputInterface $input, OutputInterface $output): int {
if ($input->getOption('uninstall')) {
$this->uninstallJSignPdf();
} else {
$this->installJSignPdf();
}
return static::SUCCESS;
}
}

View file

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace OCA\Libresign\Command\Install;
use OC\SystemConfig;
use OCA\Libresign\Command\Base;
use OCP\Files\IRootFolder;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\ITempManager;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class Java extends Base {
public function __construct(
ITempManager $tempManager,
IClientService $clientService,
IConfig $config,
SystemConfig $systemConfig,
IRootFolder $rootFolder
) {
parent::__construct(
$tempManager,
$clientService,
$config,
$systemConfig,
$rootFolder
);
}
protected function configure(): void {
$this
->setName('libresign:install:java')
->setDescription('Download and configure Java')
->addOption('uninstall',
null,
InputOption::VALUE_NONE,
'Uninstall standalone Java binaries'
);
}
protected function execute(InputInterface $input, OutputInterface $output): int {
if ($input->getOption('uninstall')) {
$this->uninstallJava();
} else {
$this->installJava();
}
return static::SUCCESS;
}
}

View file

@ -15,6 +15,7 @@ class JSignPdfHandler extends SignEngineHandler {
private $jSignParam;
/** @var IConfig */
private $config;
const VERSION = '2.0.0';
public function __construct(
IConfig $config
@ -41,14 +42,18 @@ class JSignPdfHandler extends SignEngineHandler {
*/
public function getJSignParam(): JSignParam {
if (!$this->jSignParam) {
$javaPath = $this->config->getAppValue(Application::APP_ID, 'java_path');
$this->jSignParam = (new JSignParam())
->setTempPath(
$this->config->getAppValue(Application::APP_ID, 'jsignpdf_temp_path', '/tmp/')
)
->setIsUseJavaInstalled(true)
->setIsUseJavaInstalled(empty($javaPath))
->setjSignPdfJarPath(
$this->config->getAppValue(Application::APP_ID, 'jsignpdf_jar_path', '/opt/jsignpdf-2.0.0/JSignPdf.jar')
$this->config->getAppValue(Application::APP_ID, 'jsignpdf_jar_path', '/opt/jsignpdf-' . self::VERSION . '/JSignPdf.jar')
);
if (!empty($javaPath)) {
$this->jSignParam->setJavaPath($javaPath);
}
}
return $this->jSignParam;
}

View file

@ -10,6 +10,7 @@ use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelLow;
use Endroid\QrCode\Matrix\Matrix;
use Endroid\QrCode\QrCode;
use Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeMargin;
use OC\SystemConfig;
use OCA\Libresign\AppInfo\Application;
use OCA\Libresign\Exception\LibresignException;
use OCA\Libresign\Service\FolderService;
@ -28,6 +29,8 @@ class Pkcs12Handler extends SignEngineHandler {
private $JSignPdfHandler;
/** @var IConfig */
private $config;
/** @var SystemConfig */
private $systemConfig;
/** @var CfsslHandler */
private $cfsslHandler;
/** @var IL10N */
@ -39,11 +42,13 @@ class Pkcs12Handler extends SignEngineHandler {
public function __construct(
FolderService $folderService,
IConfig $config,
SystemConfig $systemConfig,
CfsslHandler $cfsslHandler,
IL10N $l10n
) {
$this->folderService = $folderService;
$this->config = $config;
$this->systemConfig = $systemConfig;
$this->cfsslHandler = $cfsslHandler;
$this->l10n = $l10n;
}
@ -269,6 +274,13 @@ class Pkcs12Handler extends SignEngineHandler {
if (!$this->cfsslHandler->getCfsslUri()) {
$this->cfsslHandler->setCfsslUri($this->config->getAppValue(Application::APP_ID, 'cfsslUri'));
}
if (!$this->cfsslHandler->getBinary()) {
$binary = $this->config->getAppValue(Application::APP_ID, 'cfssl_bin');
$instanceId = $this->systemConfig->getValue('instanceid', null);
$this->cfsslHandler->setBinary(
'appdata_' . $instanceId . '/' . Application::APP_ID . '/' . $binary
);
}
return $this->cfsslHandler;
}

View file

@ -2,6 +2,7 @@
namespace OCA\Libresign\Service;
use OC\SystemConfig;
use OCA\Libresign\AppInfo\Application;
use OCA\Libresign\Handler\CfsslHandler;
use OCA\Libresign\Handler\CfsslServerHandler;
@ -12,18 +13,21 @@ class AdminSignatureService {
private $cfsslServerHandler;
/** @var CfsslHandler */
private $cfsslHandler;
/** @var IConfig */
private $config;
/** @var SystemConfig */
private $systemConfig;
public function __construct(
CfsslServerHandler $cfsslServerHandler,
CfsslHandler $cfsslHandler,
IConfig $config
IConfig $config,
SystemConfig $systemConfig
) {
$this->cfsslServerHandler = $cfsslServerHandler;
$this->cfsslHandler = $cfsslHandler;
$this->config = $config;
$this->systemConfig = $systemConfig;
}
public function generate(
@ -32,7 +36,8 @@ class AdminSignatureService {
string $organization,
string $organizationUnit,
string $cfsslUri,
string $configPath
string $configPath,
string $binary = null
): void {
$key = bin2hex(random_bytes(16));
@ -42,8 +47,20 @@ class AdminSignatureService {
$organization,
$organizationUnit,
$key,
$configPath
$configPath,
$binary
);
if ($binary) {
$this->cfsslHandler
->setBinary(
$this->systemConfig->getValue('datadirectory', \OC::$SERVERROOT . DIRECTORY_SEPARATOR . 'data') . DIRECTORY_SEPARATOR .
'appdata_' . $this->systemConfig->getValue('instanceid', null) . DIRECTORY_SEPARATOR .
Application::APP_ID . DIRECTORY_SEPARATOR .
'cfssl'
);
}
$this->cfsslHandler
->setCfsslUri($cfsslUri);
for ($i = 1;$i <= 4;$i++) {
if ($this->cfsslHandler->health($cfsslUri)) {
break;