Skip to content

Commit cbe8033

Browse files
committed
Merge branch 'api_token_encryption_3_4_0-846' into 'main'
Encrypt API token See merge request softwares-pkp/plugins_ojs/dataverse!211
2 parents dc0ee7c + 8b2d09d commit cbe8033

File tree

14 files changed

+206
-4
lines changed

14 files changed

+206
-4
lines changed

.gitlab-ci.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ include:
99
- 'templates/groups/ojs/unit_tests.yml'
1010
- 'templates/groups/ojs/cypress_tests.yml'
1111

12+
.integration_tests_template:
13+
before_script:
14+
- sed -i "s/api_key_secret = \"\"/api_key_secret = \"$API_KEY_SECRET\"/" /var/www/$APPLICATION/config.inc.php
15+
1216
# Desabilita os jobs de testes de aceitação da aplicação, por enquanto
1317
ojs_integration_tests:
1418
rules:
15-
- when: never
19+
- when: never

DataverseSettingsForm.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PKP\form\validation\FormValidator;
1212
use PKP\form\validation\FormValidatorCustom;
1313
use PKP\form\validation\FormValidatorPost;
14+
use APP\plugins\generic\dataverse\classes\DataEncryption;
1415
use APP\plugins\generic\dataverse\classes\exception\DataverseException;
1516
use APP\plugins\generic\dataverse\classes\dataverseConfiguration\DataverseConfiguration;
1617
use APP\plugins\generic\dataverse\classes\dataverseConfiguration\DataverseConfigurationDAO;
@@ -31,7 +32,9 @@ class DataverseSettingsForm extends Form
3132

3233
public function __construct(Plugin $plugin, int $contextId)
3334
{
34-
parent::__construct($plugin->getTemplateResource('dataverseConfigurationForm.tpl'));
35+
$encryption = new DataEncryption();
36+
$template = $encryption->secretConfigExists() ? 'dataverseConfigurationForm.tpl' : 'emptySecretKey.tpl';
37+
parent::__construct($plugin->getTemplateResource($template));
3538

3639
$this->plugin = $plugin;
3740
$this->contextId = $contextId;
@@ -103,6 +106,7 @@ public function fetch($request, $template = null, $display = false)
103106
public function execute(...$functionArgs)
104107
{
105108
$this->setDefaultAdditionalInstructions();
109+
$this->encryptApiToken();
106110
foreach (self::CONFIG_VARS as $configVar => $type) {
107111
$this->plugin->updateSetting($this->contextId, $configVar, $this->getData($configVar), $type);
108112
}
@@ -140,4 +144,15 @@ private function setDefaultAdditionalInstructions(): void
140144

141145
$this->setData('additionalInstructions', $additionalInstructions);
142146
}
147+
148+
private function encryptApiToken(): void
149+
{
150+
$encryption = new DataEncryption();
151+
$apiToken = $this->getData('apiToken');
152+
153+
if (!$encryption->textIsEncrypted($apiToken)) {
154+
$encryptedToken = $encryption->encryptString($apiToken);
155+
$this->setData('apiToken', $encryptedToken);
156+
}
157+
}
143158
}

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ Check the latest compatible version for your application on the [Releases page](
1515

1616
All versions are compatible with Dataverse 5.x and 6.x.
1717

18+
## Requirements for usage
19+
20+
1. **api_key_secret**
21+
22+
The OJS instance must have the `api_key_secret` configuration set up, you may contact your system administrator to do that (see [this post](https://forum.pkp.sfu.ca/t/how-to-generate-a-api-key-secret-code-in-ojs-3/72008)).
23+
24+
This is required to use the API credentials provided, that are stored encrypted in the OJS database.
25+
1826
## Installation
1927

2028
This plugin is available for installation via the [PKP Plugin Gallery](https://docs.pkp.sfu.ca/plugin-inventory/en/). For installation, follow these steps:

classes/DataEncryption.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
namespace APP\plugins\generic\dataverse\classes;
4+
5+
use PKP\config\Config;
6+
use Illuminate\Encryption\Encrypter;
7+
use Exception;
8+
9+
class DataEncryption
10+
{
11+
private const ENCRYPTION_CIPHER = 'aes-256-cbc';
12+
private const BASE64_PREFIX = 'base64:';
13+
14+
public function secretConfigExists(): bool
15+
{
16+
try {
17+
$this->getSecretFromConfig();
18+
} catch (Exception $e) {
19+
return false;
20+
}
21+
return true;
22+
}
23+
24+
private function getSecretFromConfig(): string
25+
{
26+
$secret = Config::getVar('security', 'api_key_secret');
27+
if ($secret === "") {
28+
throw new Exception("Dataverse Error: A secret must be set in the config file ('api_key_secret') so that keys can be encrypted and decrypted");
29+
}
30+
31+
return $this->normalizeSecret($secret);
32+
}
33+
34+
private function normalizeSecret(string $secret): string
35+
{
36+
return hash('sha256', $secret, true);
37+
}
38+
39+
public function textIsEncrypted(string $text): bool
40+
{
41+
if (!str_starts_with($text, self::BASE64_PREFIX)) {
42+
return false;
43+
}
44+
45+
try {
46+
$this->decryptString($text);
47+
return true;
48+
} catch (Exception $e) {
49+
return false;
50+
}
51+
}
52+
53+
public function encryptString(string $plainText): string
54+
{
55+
$secret = $this->getSecretFromConfig();
56+
$encrypter = new Encrypter($secret, self::ENCRYPTION_CIPHER);
57+
58+
try {
59+
$encryptedString = $encrypter->encrypt($plainText);
60+
} catch (Exception $e) {
61+
throw new Exception("DEIA Survey - Failed to encrypt string");
62+
}
63+
64+
return self::BASE64_PREFIX . base64_encode($encryptedString);
65+
}
66+
67+
public function decryptString(string $encryptedText): string
68+
{
69+
$secret = $this->getSecretFromConfig();
70+
$encrypter = new Encrypter($secret, self::ENCRYPTION_CIPHER);
71+
72+
$encryptedText = str_replace(self::BASE64_PREFIX, '', $encryptedText);
73+
$payload = base64_decode($encryptedText);
74+
75+
try {
76+
$decryptedString = $encrypter->decrypt($payload);
77+
} catch (Exception $e) {
78+
throw new Exception("Dataverse Error: Failed to decrypt string");
79+
}
80+
81+
return $decryptedString;
82+
}
83+
}

classes/dataverseConfiguration/DataverseConfigurationDAO.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Illuminate\Support\Facades\DB;
66
use PKP\db\DAORegistry;
77
use APP\plugins\generic\dataverse\classes\dataverseConfiguration\DataverseConfiguration;
8+
use APP\plugins\generic\dataverse\classes\DataEncryption;
89

910
class DataverseConfigurationDAO
1011
{
@@ -36,6 +37,7 @@ public function hasConfiguration(int $contextId): bool
3637
public function get(int $contextId): DataverseConfiguration
3738
{
3839
$settings = $this->dao->getPluginSettings($contextId, $this->pluginName);
40+
$settings = $this->decryptApiToken($settings);
3941
$configuration = $this->newDataObject();
4042
$configuration->setAllData($settings);
4143
return $configuration;
@@ -53,4 +55,15 @@ public function insert(int $contextId, DataverseConfiguration $configuration): v
5355
);
5456
}
5557
}
58+
59+
private function decryptApiToken(array $settings): array
60+
{
61+
if (isset($settings['apiToken'])) {
62+
$encryption = new DataEncryption();
63+
if ($encryption->textIsEncrypted($settings['apiToken'])) {
64+
$settings['apiToken'] = $encryption->decryptString($settings['apiToken']);
65+
}
66+
}
67+
return $settings;
68+
}
5669
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace APP\plugins\generic\dataverse\classes\migrations;
4+
5+
use Illuminate\Database\Migrations\Migration;
6+
use Illuminate\Support\Facades\DB;
7+
use APP\plugins\generic\dataverse\classes\DataEncryption;
8+
9+
class APITokenEncryptionMigration extends Migration
10+
{
11+
public function up(): void
12+
{
13+
$encrypter = new DataEncryption();
14+
if (!$encrypter->secretConfigExists()) {
15+
return;
16+
}
17+
18+
DB::table('plugin_settings')
19+
->where('plugin_name', 'dataverseplugin')
20+
->where('setting_name', 'apiToken')
21+
->get(['context_id', 'setting_value'])
22+
->each(function ($row) use ($encrypter) {
23+
if (empty($row->setting_value) || $encrypter->textIsEncrypted($row->setting_value)) {
24+
return;
25+
}
26+
27+
$encryptedValue = $encrypter->encryptString($row->setting_value);
28+
DB::table('plugin_settings')
29+
->where('plugin_name', 'dataverseplugin')
30+
->where('context_id', $row->context_id)
31+
->where('setting_name', 'apiToken')
32+
->update(['setting_value' => $encryptedValue]);
33+
});
34+
}
35+
36+
}

docs/README-es.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ Este plugin es compatible con las siguientes aplicaciones PKP:
1313

1414
Consulte la última versión compatible con su aplicación en la [Página de Versiones](https://github.com/lepidus/dataversePlugin/releases).
1515

16+
## Requisitos para uso
17+
18+
1. **api_key_secret**
19+
20+
La instancia de OJS debe tener la configuración `api_key_secret` configurada, puedes contactar a tu administrador de sistema para hacerlo (ver [esta publicación](https://forum.pkp.sfu.ca/t/how-to-generate-a-api-key-secret-code-in-ojs-3/72008)).
21+
22+
Esto es necesario para utilizar las credenciales de API proporcionadas, que se almacenan cifradas en la base de datos de OJS.
23+
1624
## Instalación
1725

1826
### Instrucciones

docs/README-pt_BR.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ Verifique a última versão compatível com a sua aplicação na [Página de Ver
1717

1818
Todas as versões são compatíveis com Dataverse 5.x e 6.x.
1919

20+
## Requisitos para uso
21+
22+
1. **api_key_secret**
23+
24+
A instância do OJS deve ter a configuração `api_key_secret` configurada, você pode contatar o administrador do sistema para fazer isso (consulte [este post](https://forum.pkp.sfu.ca/t/how-to-generate-a-api-key-secret-code-in-ojs-3/72008)).
25+
26+
Isso é necessário para utilizar as credenciais de API fornecidas, que são armazenadas criptografadas no banco de dados do OJS.
27+
2028
## Instalação
2129

2230
Este plugin está disponível para instalação através da [Galeria de Plugins da PKP](https://docs.pkp.sfu.ca/plugin-inventory/en/). Para fazer a instalação, siga os seguintes passos:

locale/en/locale.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ msgstr "Dataverse Plugin"
1717
msgid "plugins.generic.dataverse.description"
1818
msgstr "Deposit data sets and/or other supplementary files to a Dataverse."
1919

20+
msgid "plugins.generic.dataverse.settings.emptyApiSecretKey"
21+
msgstr "The administrator must set a secret in the site configuration file ('api_key_secret')."
22+
2023
msgid "plugins.generic.dataverse.settings.description"
2124
msgstr ""
2225
"Configure the Dataverse API to deposit research data into a Dataverse repository.<br>"

locale/es/locale.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ msgstr "Módulo Dataverse"
1717
msgid "plugins.generic.dataverse.description"
1818
msgstr "Deposita conjuntos de datos y/u otros documentos complementarios en Dataverse."
1919

20+
msgid "plugins.generic.dataverse.settings.emptyApiSecretKey"
21+
msgstr "Es necesario que el administrador establezca un secreto en el archivo de configuración del sitio ('api_key_secret')."
22+
2023
msgid "plugins.generic.dataverse.settings.description"
2124
msgstr ""
2225
"Configure la API Dataverse para depositar datos de investigación en un repositorio de Dataverse.<br>"

0 commit comments

Comments
 (0)