Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable30] Digitally sign documents via software certificates #4170

Merged
merged 3 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions css/admin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ input#zoteroAPIKeyField {
width: 300px;
}

textarea#documentSigningCertField {
width: 600px;
}

textarea#documentSigningKeyField {
width: 600px;
}

textarea#documentSigningCaField {
width: 600px;
}

#richdocuments,
#richdocuments-templates {
// inline buttons on section headers
Expand Down
30 changes: 29 additions & 1 deletion lib/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,10 @@ public function updateWatermarkSettings($settings = []): JSONResponse {
* @return JSONResponse
*/
public function setPersonalSettings($templateFolder,
$zoteroAPIKeyInput) {
$zoteroAPIKeyInput,
$documentSigningCertInput,
$documentSigningKeyInput,
$documentSigningCaInput) {
$message = $this->l10n->t('Saved');
$status = 'success';

Expand All @@ -256,6 +259,31 @@ public function setPersonalSettings($templateFolder,
}
}

if ($documentSigningCertInput !== null) {
try {
$this->config->setUserValue($this->userId, 'richdocuments', 'documentSigningCert', $documentSigningCertInput);
} catch (PreConditionNotMetException $e) {
$message = $this->l10n->t('Error when saving');
$status = 'error';
}
}
if ($documentSigningKeyInput !== null) {
try {
$this->config->setUserValue($this->userId, 'richdocuments', 'documentSigningKey', $documentSigningKeyInput);
} catch (PreConditionNotMetException $e) {
$message = $this->l10n->t('Error when saving');
$status = 'error';
}
}
if ($documentSigningCaInput !== null) {
try {
$this->config->setUserValue($this->userId, 'richdocuments', 'documentSigningCa', $documentSigningCaInput);
} catch (PreConditionNotMetException $e) {
$message = $this->l10n->t('Error when saving');
$status = 'error';
}
}

$response = [
'status' => $status,
'data' => ['message' => $message]
Expand Down
9 changes: 9 additions & 0 deletions lib/Controller/WopiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,15 @@ public function checkFileInfo($fileId, $access_token) {
$zoteroAPIKey = $this->config->getUserValue($wopi->getEditorUid(), 'richdocuments', 'zoteroAPIKey', '');
$response['UserPrivateInfo']['ZoteroAPIKey'] = $zoteroAPIKey;
}
$enableDocumentSigning = $this->config->getAppValue(Application::APPNAME, 'documentSigningEnabled', 'yes') === 'yes';
if (!$isPublic && $enableDocumentSigning) {
$documentSigningCert = $this->config->getUserValue($wopi->getEditorUid(), 'richdocuments', 'documentSigningCert', '');
$response['UserPrivateInfo']['SignatureCert'] = $documentSigningCert;
$documentSigningKey = $this->config->getUserValue($wopi->getEditorUid(), 'richdocuments', 'documentSigningKey', '');
$response['UserPrivateInfo']['SignatureKey'] = $documentSigningKey;
$documentSigningCa = $this->config->getUserValue($wopi->getEditorUid(), 'richdocuments', 'documentSigningCa', '');
$response['UserPrivateInfo']['SignatureCa'] = $documentSigningCa;
}
if ($wopi->hasTemplateId()) {
$response['TemplateSource'] = $this->getWopiUrlForTemplate($wopi);
}
Expand Down
4 changes: 4 additions & 0 deletions lib/Service/CapabilitiesService.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ public function hasWASMSupport(): bool {
return $this->getCapabilities()['hasWASMSupport'] ?? false;
}

public function hasDocumentSigningSupport(): bool {
return $this->getCapabilities()['hasDocumentSigningSupport'] ?? false;
}

public function hasFormFilling(): bool {
return $this->isVersionAtLeast('24.04.5.2');
}
Expand Down
4 changes: 4 additions & 0 deletions lib/Settings/Personal.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ public function getForm() {
'personal',
[
'templateFolder' => $this->config->getUserValue($this->userId, 'richdocuments', 'templateFolder', ''),
'hasDocumentSigningSupport' => $this->capabilitiesService->hasDocumentSigningSupport(),
'documentSigningCert' => $this->config->getUserValue($this->userId, 'richdocuments', 'documentSigningCert', ''),
'documentSigningKey' => $this->config->getUserValue($this->userId, 'richdocuments', 'documentSigningKey', ''),
'documentSigningCa' => $this->config->getUserValue($this->userId, 'richdocuments', 'documentSigningCa', ''),
'hasZoteroSupport' => $this->capabilitiesService->hasZoteroSupport(),
'zoteroAPIKey' => $this->config->getUserValue($this->userId, 'richdocuments', 'zoteroAPIKey', '')
],
Expand Down
77 changes: 77 additions & 0 deletions src/personal.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ import { showError } from '@nextcloud/dialogs'
this.zoteroAPIKeySaveButton = document.getElementById('zoteroAPIKeySave')
this.zoteroAPIKeyRemoveButton = document.getElementById('zoteroAPIKeyRemove')

this.documentSigningCertInput = document.getElementById('documentSigningCertField')
this.documentSigningCertSaveButton = document.getElementById('documentSigningCertSave')
this.documentSigningCertRemoveButton = document.getElementById('documentSigningCertRemove')
this.documentSigningKeyInput = document.getElementById('documentSigningKeyField')
this.documentSigningKeySaveButton = document.getElementById('documentSigningKeySave')
this.documentSigningKeyRemoveButton = document.getElementById('documentSigningKeyRemove')
this.documentSigningCaInput = document.getElementById('documentSigningCaField')
this.documentSigningCaSaveButton = document.getElementById('documentSigningCaSave')
this.documentSigningCaRemoveButton = document.getElementById('documentSigningCaRemove')

const self = this
this.templateSelectButton.addEventListener('click', function() {
OC.dialogs.filepicker(t('richdocuments', 'Select a personal template folder'), function(datapath, returntype) {
Expand All @@ -31,6 +41,19 @@ import { showError } from '@nextcloud/dialogs'
})

this.zoteroAPIKeyRemoveButton.addEventListener('click', this.resetZoteroAPI.bind(this))

this.documentSigningCertSaveButton.addEventListener('click', function() {
self.updateDocumentSigningCert(self.documentSigningCertInput.value)
})
this.documentSigningCertRemoveButton.addEventListener('click', this.resetDocumentSigningCert.bind(this))
this.documentSigningKeySaveButton.addEventListener('click', function() {
self.updateDocumentSigningKey(self.documentSigningKeyInput.value)
})
this.documentSigningKeyRemoveButton.addEventListener('click', this.resetDocumentSigningKey.bind(this))
this.documentSigningCaSaveButton.addEventListener('click', function() {
self.updateDocumentSigningCa(self.documentSigningCaInput.value)
})
this.documentSigningCaRemoveButton.addEventListener('click', this.resetDocumentSigningCa.bind(this))
}

PersonalSettings.prototype.updateSetting = function(path) {
Expand Down Expand Up @@ -69,6 +92,60 @@ import { showError } from '@nextcloud/dialogs'
})
}

PersonalSettings.prototype.updateDocumentSigningCert = function(ca) {
const self = this
this._updateSetting({ documentSigningCertInput: ca }, function() {
self.documentSigningCertInput.value = ca
}, function() {
showError(t('richdocuments', 'Failed to update the document signing CA chain'))
})
}

PersonalSettings.prototype.resetDocumentSigningCert = function() {
const self = this
this._updateSetting({ documentSigningCertInput: '' }, function() {
self.documentSigningCertInput.value = ''
}, function() {

})
}

PersonalSettings.prototype.updateDocumentSigningKey = function(ca) {
const self = this
this._updateSetting({ documentSigningKeyInput: ca }, function() {
self.documentSigningKeyInput.value = ca
}, function() {
showError(t('richdocuments', 'Failed to update the document signing CA chain'))
})
}

PersonalSettings.prototype.resetDocumentSigningKey = function() {
const self = this
this._updateSetting({ documentSigningKeyInput: '' }, function() {
self.documentSigningKeyInput.value = ''
}, function() {

})
}

PersonalSettings.prototype.updateDocumentSigningCa = function(ca) {
const self = this
this._updateSetting({ documentSigningCaInput: ca }, function() {
self.documentSigningCaInput.value = ca
}, function() {
showError(t('richdocuments', 'Failed to update the document signing CA chain'))
})
}

PersonalSettings.prototype.resetDocumentSigningCa = function() {
const self = this
this._updateSetting({ documentSigningCaInput: '' }, function() {
self.documentSigningCaInput.value = ''
}, function() {

})
}

PersonalSettings.prototype._updateSetting = function(data, successCallback, errorCallback) {
OC.msg.startAction('#documents-admin-msg', t('richdocuments', 'Saving …'))
const request = new XMLHttpRequest()
Expand Down
23 changes: 23 additions & 0 deletions templates/personal.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,28 @@
<?php } else { ?>
<p><em><?php p($l->t('This instance does not support Zotero, because the feature is missing or disabled. Please contact the administration.')); ?></em></p>
<?php } ?>
<p><strong><?php p($l->t('Document signing')) ?></strong></p>
<?php if ($_['hasDocumentSigningSupport']) { ?>
<div class="input-wrapper">
<p><label for="documentSigningCertField"><?php p($l->t('Enter document signing cert (in PEM format)')); ?></label><br />
<textarea type="text" name="documentSigningCertField" id="documentSigningCertField"><?php p($_['documentSigningCert']); ?></textarea><br />
<button id="documentSigningCertSave"><span title="<?php p($l->t('Save document signing cert')); ?>" data-toggle="tooltip">Save</span></button>
<button id="documentSigningCertRemove"><span class="icon-delete" title="<?php p($l->t('Remove document signing cert')); ?>" data-toggle="tooltip"></span></button>
</p>
<p><label for="documentSigningKeyField"><?php p($l->t('Enter document signing key')); ?></label><br />
<textarea type="text" name="documentSigningKeyField" id="documentSigningKeyField"><?php p($_['documentSigningKey']); ?></textarea><br />
<button id="documentSigningKeySave"><span title="<?php p($l->t('Save document signing key')); ?>" data-toggle="tooltip">Save</span></button>
<button id="documentSigningKeyRemove"><span class="icon-delete" title="<?php p($l->t('Remove document signing key')); ?>" data-toggle="tooltip"></span></button>
</p>
<p><label for="documentSigningCaField"><?php p($l->t('Enter document signing CA chain')); ?></label><br />
<textarea type="text" name="documentSigningCaField" id="documentSigningCaField"><?php p($_['documentSigningCa']); ?></textarea><br />
<button id="documentSigningCaSave"><span title="<?php p($l->t('Save document signing CA chain')); ?>" data-toggle="tooltip">Save</span></button>
<button id="documentSigningCaRemove"><span class="icon-delete" title="<?php p($l->t('Remove document signing CA chain')); ?>" data-toggle="tooltip"></span></button>
</p>
<p><em><?php p($l->t('To use document signing, specify your signing certificate, key and CA chain here.')); ?></em></p>
</div>
<?php } else { ?>
<p><em><?php p($l->t('This instance does not support document signing, because the feature is missing or disabled. Please contact the administrator.')); ?></em></p>
<?php } ?>
</div>
</div>
Loading