diff --git a/CHANGELOG.md b/CHANGELOG.md index c996a0a5..9417cad7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Change Log +## 1.0.5 +### Added +- default name for new file +- getting default value from server config +- checking the encryption module + +### Changed +- included editing for csv format +- fix track activities and versions + +### Security +- jwt signature for inbox request from Document Server + ## 1.0.4 ### Added - advanced server settings for specifying internal addresses diff --git a/README.md b/README.md index 58ef6b7e..e614723a 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # ![](screenshots/icon.png) ownCloud/Nextcloud ONLYOFFICE integration app -This app enables users to edit office documents from [ownCloud](https://owncloud.com)/[Nextcloud](https://nextcloud.com) using ONLYOFFICE Document Server. Currently the following document formats can be edited with this app: DOCX, XLSX, PPTX, TXT. The above mentioned formats are also available for viewing together with PDF and CSV. The edited files of the corresponding type can be converted into the Office Open XML formats: ODT, ODS, ODP, DOC, XLS, PPT, PPS, EPUB, RTF, HTML, HTM. +This app enables users to edit office documents from [ownCloud](https://owncloud.com)/[Nextcloud](https://nextcloud.com) using ONLYOFFICE Document Server. Currently the following document formats can be edited with this app: DOCX, XLSX, PPTX, TXT, CSV. The above mentioned formats are also available for viewing together with PDF. The edited files of the corresponding type can be converted into the Office Open XML formats: ODT, ODS, ODP, DOC, XLS, PPT, PPS, EPUB, RTF, HTML, HTM. The app will create an item in the `new` (+) menu to create **Document**, **Spreadsheet**, **Presentation**. It will also create a new **Open in ONLYOFFICE** menu option within the document library for Office documents. This allows multiple users to collaborate in real time and to save back those changes to ownCloud/Nextcloud. - +You can also use our **[Docker installation](https://github.com/ONLYOFFICE/docker-onlyoffice-owncloud)** to get installed and configured Document Server and ownCloud installation with a couple of commands. ## Installing ONLYOFFICE Document Server -You will need an instance of ONLYOFFICE Document Server that is resolvable and connectable both from ownCloud/Nextcloud and any end clients (version 4.2 and later are supported for use with the app). If that is not the case, use the official ONLYOFFICE Document Server documetnations page: [Document Server for Linux](http://helpcenter.onlyoffice.com/server/linux/document/linux-installation.aspx). ONLYOFFICE Document Server must also be able to POST to ownCloud/Nextcloud directly. +You will need an instance of ONLYOFFICE Document Server that is resolvable and connectable both from ownCloud/Nextcloud and any end clients (version 4.2.7 and later are supported for use with the app). If that is not the case, use the official ONLYOFFICE Document Server documetnations page: [Document Server for Linux](http://helpcenter.onlyoffice.com/server/linux/document/linux-installation.aspx). ONLYOFFICE Document Server must also be able to POST to ownCloud/Nextcloud directly. The easiest way to start an instance of ONLYOFFICE Document Server is to use [Docker](https://github.com/ONLYOFFICE/Docker-DocumentServer). @@ -18,13 +18,18 @@ The easiest way to start an instance of ONLYOFFICE Document Server is to use [Do To start using ONLYOFFICE Document Server with ownCloud/Nextcloud, the following steps must be performed: -1. Place ownCloud/Nextcloud ONLYOFFICE integration app to your ownCloud/Nextcloud server into the _/apps_ (or some other) directory, [used](https://doc.owncloud.org/server/9.0/admin_manual/installation/apps_management_installation.html#using-custom-app-directories) to connect applications: +1. Place ownCloud/Nextcloud ONLYOFFICE integration app to your ownCloud/Nextcloud server into the _apps/_ (or some other) directory, [used](https://doc.owncloud.org/server/9.0/admin_manual/installation/apps_management_installation.html#using-custom-app-directories) to connect applications: ``` cd apps/ git clone https://github.com/ONLYOFFICE/onlyoffice-owncloud.git onlyoffice ``` -2. In ownCloud/Nextcloud open the `~/index.php/settings/apps?category=disabled` page with _Not enabled_ apps by administrator and click _Enable_ for the **ONLYOFFICE** application. +2. Change the owner to update the application right from ownCloud/Nextcloud web interface: +``` +chown -R www-data:www-data onlyoffice +``` + +3. In ownCloud/Nextcloud open the `~/index.php/settings/apps?category=disabled` page with _Not enabled_ apps by administrator and click _Enable_ for the **ONLYOFFICE** application. @@ -33,7 +38,7 @@ git clone https://github.com/ONLYOFFICE/onlyoffice-owncloud.git onlyoffice In ownCloud/Nextcloud open the `~/index.php/settings/admin#onlyoffice` page with administrative settings for **ONLYOFFICE** section. Enter the following address to connect ONLYOFFICE Document Server: ``` -https:// +https:/// ``` Where the **documentserver** is the name of the server with the ONLYOFFICE Document Server installed. The address must be accessible for the user browser and from the ownCloud/Nextcloud server. The ownCloud/Nextcloud server address must also be accessible from ONLYOFFICE Document Server for correct work. diff --git a/appinfo/application.php b/appinfo/application.php index 8e8d6d0e..52b815f9 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -60,19 +60,18 @@ public function __construct(array $urlParams = []) { $this->crypt = new Crypt($this->appConfig); // Default script and style if configured - if (!empty($this->appConfig->GetDocumentServerUrl()) - && array_key_exists("REQUEST_URI", \OC::$server->getRequest()->server)) - { - $url = \OC::$server->getRequest()->server["REQUEST_URI"]; - - if (isset($url)) { - if (preg_match("%/apps/files(/.*)?%", $url)) { - Util::addScript($appName, "main"); - Util::addStyle($appName, "main"); + $eventDispatcher = \OC::$server->getEventDispatcher(); + $eventDispatcher->addListener("OCA\Files::loadAdditionalScripts", + function() { + if (!empty($this->appConfig->GetDocumentServerUrl())) { + Util::addScript("onlyoffice", "main"); + Util::addStyle("onlyoffice", "main"); } - } - } + }); + require_once __DIR__ . "/../3rdparty/jwt/BeforeValidException.php"; + require_once __DIR__ . "/../3rdparty/jwt/ExpiredException.php"; + require_once __DIR__ . "/../3rdparty/jwt/SignatureInvalidException.php"; require_once __DIR__ . "/../3rdparty/jwt/JWT.php"; $container = $this->getContainer(); diff --git a/appinfo/info.xml b/appinfo/info.xml index e3866927..a5528b46 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -7,7 +7,7 @@ ONLYOFFICE integration app enables users to edit Office documents within ONLYOFFICE from OwnCloud. This will create a new Open in ONLYOFFICE action within the document library for Office documents. This allows multiple users to collaborate in real time and to save back those changes to OwnCloud. AGPL Ascensio System SIA - 1.0.4 + 1.0.5 Onlyoffice diff --git a/controller/callbackcontroller.php b/controller/callbackcontroller.php index 8a534f19..872d2b49 100644 --- a/controller/callbackcontroller.php +++ b/controller/callbackcontroller.php @@ -168,6 +168,23 @@ public function download($doc) { return new JSONResponse(["message" => $this->trans->t("Invalid request")], Http::STATUS_BAD_REQUEST); } + if (!empty($this->config->GetDocumentServerSecret())) { + $header = \OC::$server->getRequest()->getHeader("Authorization"); + if (empty($header)) { + $this->logger->info("Download without jwt", array("app" => $this->appName)); + return new JSONResponse(["message" => $this->trans->t("Access deny")], Http::STATUS_FORBIDDEN); + } + + $header = substr($header, strlen("Bearer ")); + + try { + $decodedHeader = \Firebase\JWT\JWT::decode($header, $this->config->GetDocumentServerSecret(), array("HS256")); + } catch (\UnexpectedValueException $e) { + $this->logger->info("Download with invalid jwt: " . $e->getMessage(), array("app" => $this->appName)); + return new JSONResponse(["message" => $this->trans->t("Access deny")], Http::STATUS_FORBIDDEN); + } + } + $fileId = $hashData->fileId; $ownerId = $hashData->ownerId; @@ -185,7 +202,7 @@ public function download($doc) { try { return new DataDownloadResponse($file->getContent(), $file->getName(), $file->getMimeType()); - } catch(\OCP\Files\NotPermittedException $e) { + } catch (\OCP\Files\NotPermittedException $e) { $this->logger->info("Download Not permitted: " . $fileId . " " . $e->getMessage(), array("app" => $this->appName)); return new JSONResponse(["message" => $this->trans->t("Not permitted")], Http::STATUS_FORBIDDEN); } @@ -216,6 +233,23 @@ public function emptyfile($doc) { return new JSONResponse(["message" => $this->trans->t("Invalid request")], Http::STATUS_BAD_REQUEST); } + if (!empty($this->config->GetDocumentServerSecret())) { + $header = \OC::$server->getRequest()->getHeader("Authorization"); + if (empty($header)) { + $this->logger->info("Download empty without jwt", array("app" => $this->appName)); + return new JSONResponse(["message" => $this->trans->t("Access deny")], Http::STATUS_FORBIDDEN); + } + + $header = substr($header, strlen("Bearer ")); + + try { + $decodedHeader = \Firebase\JWT\JWT::decode($header, $this->config->GetDocumentServerSecret(), array("HS256")); + } catch (\UnexpectedValueException $e) { + $this->logger->info("Download empty with invalid jwt: " . $e->getMessage(), array("app" => $this->appName)); + return new JSONResponse(["message" => $this->trans->t("Access deny")], Http::STATUS_FORBIDDEN); + } + } + $templatePath = dirname(__DIR__) . DIRECTORY_SEPARATOR . "assets" . DIRECTORY_SEPARATOR . "en" . DIRECTORY_SEPARATOR . "new.docx"; $template = file_get_contents($templatePath); @@ -226,7 +260,7 @@ public function emptyfile($doc) { try { return new DataDownloadResponse($template, "new.docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); - } catch(\OCP\Files\NotPermittedException $e) { + } catch (\OCP\Files\NotPermittedException $e) { $this->logger->info("Download Not permitted: " . $fileId . " " . $e->getMessage(), array("app" => $this->appName)); return new JSONResponse(["message" => $this->trans->t("Not permitted")], Http::STATUS_FORBIDDEN); } @@ -260,6 +294,30 @@ public function track($doc, $users, $key, $status, $url) { return new JSONResponse(["message" => $this->trans->t("Invalid request")], Http::STATUS_BAD_REQUEST); } + if (!empty($this->config->GetDocumentServerSecret())) { + $header = \OC::$server->getRequest()->getHeader("Authorization"); + if (empty($header)) { + $this->logger->info("Track without jwt", array("app" => $this->appName)); + return new JSONResponse(["message" => $this->trans->t("Access deny")], Http::STATUS_FORBIDDEN); + } + + $header = substr($header, strlen("Bearer ")); + + try { + $decodedHeader = \Firebase\JWT\JWT::decode($header, $this->config->GetDocumentServerSecret(), array("HS256")); + $this->logger->debug("Track HEADER : " . json_encode($decodedHeader), array("app" => $this->appName)); + + $payload = $decodedHeader->payload; + $users = isset($payload->users) ? $payload->users : NULL; + $key = $payload->key; + $status = $payload->status; + $url = isset($payload->url) ? $payload->url : NULL; + } catch (\UnexpectedValueException $e) { + $this->logger->info("Track with invalid jwt: " . $e->getMessage(), array("app" => $this->appName)); + return new JSONResponse(["message" => $this->trans->t("Access deny")], Http::STATUS_FORBIDDEN); + } + } + $trackerStatus = $this->_trackerStatus[$status]; $error = 1; @@ -270,6 +328,9 @@ public function track($doc, $users, $key, $status, $url) { $fileId = $hashData->fileId; $ownerId = $hashData->ownerId; + \OC_Util::tearDownFS(); + \OC_Util::setupFS($ownerId); + $files = $this->root->getUserFolder($ownerId)->getById($fileId); if (empty($files)) { $this->logger->info("Files for track not found: " . $fileId, array("app" => $this->appName)); @@ -308,7 +369,7 @@ public function track($doc, $users, $key, $status, $url) { $from = $parsedUrl["scheme"] . "://" . $parsedUrl["host"] . (array_key_exists("port", $parsedUrl) ? (":" . $parsedUrl["port"]) : "") . "/"; } - $this->logger->debug("Replace in track from " . $from . " to " . $this->config->GetDocumentServerInternalUrl(true)); + $this->logger->debug("Replace in track from " . $from . " to " . $this->config->GetDocumentServerInternalUrl(true), array("app" => $this->appName)); $url = str_replace($from, $this->config->GetDocumentServerInternalUrl(true), $url); } diff --git a/controller/editorcontroller.php b/controller/editorcontroller.php index 53be5383..e0b37c4b 100644 --- a/controller/editorcontroller.php +++ b/controller/editorcontroller.php @@ -290,7 +290,7 @@ public function convert($fileId) { * Print editor section * * @param integer $fileId - file identifier - * + * * @return TemplateResponse * * @NoAdminRequired diff --git a/controller/settingscontroller.php b/controller/settingscontroller.php index 906918ae..901f1bd6 100644 --- a/controller/settingscontroller.php +++ b/controller/settingscontroller.php @@ -26,6 +26,7 @@ namespace OCA\Onlyoffice\Controller; +use OCP\App; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\TemplateResponse; use OCP\IL10N; @@ -124,7 +125,8 @@ public function index() { "secret" => $this->config->GetDocumentServerSecret(), "currentServer" => $this->urlGenerator->getAbsoluteURL("/"), "defFormats" => $defFormats, - "sameTab" => $this->config->GetSameTab() + "sameTab" => $this->config->GetSameTab(), + "encryption" => $this->checkEncryptionModule() ]; return new TemplateResponse($this->appName, "settings", $data, "blank"); } @@ -162,6 +164,10 @@ public function SaveSettings($documentserver, $this->config->SetDefaultFormats($defFormats); $this->config->SetSameTab($sameTab); + if ($this->checkEncryptionModule()) { + $this->logger->info("SaveSettings when encryption is enabled", array("app" => $this->appName)); + } + return [ "documentserver" => $this->config->GetDocumentServerUrl(), "documentserverInternal" => $this->config->GetDocumentServerInternalUrl(true), @@ -251,4 +257,24 @@ private function checkDocServiceUrl() { return ""; } + + /** + * Checking encryption enabled + */ + private function checkEncryptionModule() { + if (!App::isEnabled("encryption")) { + return false; + } + if (!\OC::$server->getEncryptionManager()->isEnabled()) { + return false; + } + + $crypt = new \OCA\Encryption\Crypto\Crypt(\OC::$server->getLogger(), \OC::$server->getUserSession(), \OC::$server->getConfig(), \OC::$server->getL10N('encryption')); + $util = new \OCA\Encryption\Util(new \OC\Files\View(), $crypt, \OC::$server->getLogger(), \OC::$server->getUserSession(), \OC::$server->getConfig(), \OC::$server->getUserManager()); + if ($util->isMasterKeyEnabled()) { + return false; + } + + return true; + } } diff --git a/css/settings.css b/css/settings.css index 8f56b07d..41461f73 100644 --- a/css/settings.css +++ b/css/settings.css @@ -43,3 +43,6 @@ cursor: pointer; text-decoration: none; } +.onlyoffice-error { + color: red; +} diff --git a/js/main.js b/js/main.js index 6d438047..87853aa8 100644 --- a/js/main.js +++ b/js/main.js @@ -174,6 +174,7 @@ menu.addMenuEntry({ id: "onlyofficeDocx", displayName: t(OCA.Onlyoffice.AppName, "Document"), + templateName: t(OCA.Onlyoffice.AppName, "Document"), iconClass: "icon-onlyoffice-new-docx", fileType: "docx", actionHandler: function (name) { @@ -184,6 +185,7 @@ menu.addMenuEntry({ id: "onlyofficeXlsx", displayName: t(OCA.Onlyoffice.AppName, "Spreadsheet"), + templateName: t(OCA.Onlyoffice.AppName, "Spreadsheet"), iconClass: "icon-onlyoffice-new-xlsx", fileType: "xlsx", actionHandler: function (name) { @@ -194,6 +196,7 @@ menu.addMenuEntry({ id: "onlyofficePpts", displayName: t(OCA.Onlyoffice.AppName, "Presentation"), + templateName: t(OCA.Onlyoffice.AppName, "Presentation"), iconClass: "icon-onlyoffice-new-pptx", fileType: "pptx", actionHandler: function (name) { diff --git a/lib/adminsettings.php b/lib/adminsettings.php index 7ab7a2ae..143a9ea0 100644 --- a/lib/adminsettings.php +++ b/lib/adminsettings.php @@ -29,6 +29,7 @@ use OCP\Settings\ISettings; use OCA\Onlyoffice\AppInfo\Application; +use OCA\Onlyoffice\Controller\SettingsController; /** * Settings controller for the administration page @@ -64,7 +65,7 @@ public function getSectionID() { public function getForm() { $app = new Application(); $container = $app->getContainer(); - $response = $container->query("\OCA\Onlyoffice\Controller\SettingsController")->index(); + $response = $container->query(SettingsController::class)->index(); return $response; } diff --git a/lib/appconfig.php b/lib/appconfig.php index 1dfae92f..1a20a4dc 100644 --- a/lib/appconfig.php +++ b/lib/appconfig.php @@ -175,6 +175,15 @@ public function GetDocumentServerUrl() { if (empty($url)) { $url = $this->predefDocumentServerUrl; } + if (empty($url) && !empty($this->config->getSystemValue($this->appName))) { + $url = $this->config->getSystemValue($this->appName)[$this->_documentserver]; + } + if ($url !== "/") { + $url = rtrim($url, "/"); + if (strlen($url) > 0) { + $url = $url . "/"; + } + } return $url; } @@ -207,6 +216,9 @@ public function GetDocumentServerInternalUrl($origin) { if (empty($url)) { $url = $this->predefDocumentServerInternalUrl; } + if (empty($url) && !empty($this->config->getSystemValue($this->appName))) { + $url = $this->config->getSystemValue($this->appName)[$this->_documentserverInternal]; + } if (!$origin && empty($url)) { $url = $this->GetDocumentServerUrl(); } @@ -242,6 +254,9 @@ public function GetStorageUrl() { if (empty($url)) { $url = $this->predefStorageUrl; } + if (empty($url) && !empty($this->config->getSystemValue($this->appName))) { + $url = $this->config->getSystemValue($this->appName)[$this->_storageUrl]; + } return $url; } @@ -270,6 +285,9 @@ public function GetDocumentServerSecret() { if (empty($secret)) { $secret = $this->predefDocumentServerSecret; } + if (empty($secret) && !empty($this->config->getSystemValue($this->appName))) { + $secret = $this->config->getSystemValue($this->appName)[$this->_secret]; + } return $secret; } @@ -351,7 +369,7 @@ public function GetSameTab() { "pptx" => [ "mime" => "application/vnd.openxmlformats-officedocument.presentationml.presentation", "type" => "presentation", "edit" => true, "def" => true ], "ppsx" => [ "mime" => "application/vnd.openxmlformats-officedocument.presentationml.slideshow", "type" => "presentation", "edit" => true, "def" => true ], "txt" => [ "mime" => "text/plain", "type" => "text", "edit" => true ], - "csv" => [ "mime" => "text/csv", "type" => "spreadsheet"/*, "edit" => true*/ ], + "csv" => [ "mime" => "text/csv", "type" => "spreadsheet", "edit" => true ], "odt" => [ "mime" => "application/vnd.oasis.opendocument.text", "type" => "text", "conv" => true ], "ods" => [ "mime" => "application/vnd.oasis.opendocument.spreadsheet", "type" => "spreadsheet", "conv" => true ], "odp" => [ "mime" => "application/vnd.oasis.opendocument.presentation", "type" => "presentation", "conv" => true ], diff --git a/lib/crypt.php b/lib/crypt.php index a83a723e..1743dced 100644 --- a/lib/crypt.php +++ b/lib/crypt.php @@ -51,7 +51,7 @@ public function __construct(AppConfig $appConfig) { /** * Generate base64 hash for the object - * + * * @param array $object - object to signature hash * * @return string @@ -64,7 +64,7 @@ public function GetHash($object) { /** * Create an object from the base64 hash - * + * * @param string $hash - base64 hash * * @return array @@ -94,7 +94,7 @@ public function ReadHash($hash) { /** * Generate base64 hash for the object - * + * * @param string $primary_key - string to the signature hash * * @return string diff --git a/lib/documentservice.php b/lib/documentservice.php index d289403e..e3ee8f2d 100644 --- a/lib/documentservice.php +++ b/lib/documentservice.php @@ -78,7 +78,7 @@ public static function GenerateRevisionId($expected_key) { /** * The method is to convert the file to the required format and return the percentage of completion - * + * * @param string $document_uri - Uri for the document to convert * @param string $from_extension - Document extension * @param string $to_extension - Extension to which to convert diff --git a/settings.php b/settings.php index 93e11b28..e41fe6b3 100644 --- a/settings.php +++ b/settings.php @@ -29,11 +29,12 @@ use OCP\User; use OCA\Onlyoffice\AppInfo\Application; +use OCA\Onlyoffice\Controller\SettingsController; User::checkAdminUser(); $app = new Application(); $container = $app->getContainer(); -$response = $container->query("\OCA\Onlyoffice\Controller\SettingsController")->index(); +$response = $container->query(SettingsController::class)->index(); return $response->render(); diff --git a/templates/settings.php b/templates/settings.php index 1a0e1417..842aa94a 100644 --- a/templates/settings.php +++ b/templates/settings.php @@ -33,13 +33,20 @@

t("ONLYOFFICE Document Service Location specifies the address of the server with the document services installed. Please change the '' for the server address in the below line.")) ?>

+ +

+ t("Encryption App is enabled, the application cannot work. You can continue working with the application if you enable master key.")) ?> + +

+ +

t("Document Editing Service address")) ?>

- " placeholder="https://" type="text"> + " placeholder="https:///" type="text"> t("Advanced server settings")) ?>

t("Document Editing Service address for internal requests from the server")) ?>

- " placeholder="https://" type="text"> + " placeholder="https:///" type="text">

t("Server address for internal requests from the Document Editing Service")) ?>

" placeholder="" type="text">