feat: correct config handling, encrypt secrets

Signed-off-by: Andrey Borysenko <andrey18106x@gmail.com>
This commit is contained in:
Andrey Borysenko 2024-10-24 00:20:53 +03:00
Родитель d1796263f4
Коммит 6931c8bd9a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 934CB29F9F59B0D1
6 изменённых файлов: 78 добавлений и 30 удалений

Просмотреть файл

@ -9,7 +9,7 @@
<summary>Integration of Discourse forum and mailing list management system</summary>
<description><![CDATA[Discourse integration provides a dashboard widget displaying your important notifications
and the ability to find topics and posts with Nextcloud's unified search.]]></description>
<version>2.1.0</version>
<version>2.2.0</version>
<licence>agpl</licence>
<author>Julien Veyssier</author>
<namespace>Discourse</namespace>
@ -22,7 +22,7 @@
<bugs>https://github.com/nextcloud/integration_discourse/issues</bugs>
<screenshot>https://github.com/nextcloud/integration_discourse/raw/main/img/screenshot1.jpg</screenshot>
<dependencies>
<nextcloud min-version="28" max-version="30"/>
<nextcloud min-version="28" max-version="31"/>
</dependencies>
<settings>
<personal>OCA\Discourse\Settings\Personal</personal>

Просмотреть файл

@ -6,6 +6,7 @@
namespace OCA\Discourse\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\IURLGenerator;
use OCP\IConfig;
use OCP\IL10N;
@ -20,28 +21,32 @@ use OCA\Discourse\Service\DiscourseAPIService;
require_once __DIR__ . '/../../vendor/autoload.php';
use OCP\PreConditionNotMetException;
use OCP\Security\ICrypto;
use phpseclib\Crypt\RSA;
class ConfigController extends Controller {
public function __construct(string $appName,
IRequest $request,
private IConfig $config,
private IURLGenerator $urlGenerator,
private IL10N $l,
private DiscourseAPIService $discourseAPIService,
private ?string $userId) {
public function __construct(
string $appName,
IRequest $request,
private IConfig $config,
private ICrypto $crypto,
private IURLGenerator $urlGenerator,
private IL10N $l,
private DiscourseAPIService $discourseAPIService,
private ?string $userId
) {
parent::__construct($appName, $request);
}
/**
* set config values
* @NoAdminRequired
*
* @param array $values
* @return DataResponse
* @throws PreConditionNotMetException
*/
#[NoAdminRequired]
public function setConfig(array $values): DataResponse {
foreach ($values as $key => $value) {
$this->config->setUserValue($this->userId, Application::APP_ID, $key, $value);
@ -107,6 +112,9 @@ class ConfigController extends Controller {
$configNonce = $this->config->getUserValue($this->userId, Application::APP_ID, 'nonce');
// decrypt payload
$privKey = $this->config->getAppValue(Application::APP_ID, 'private_key');
if ($privKey !== '') {
$privKey = $this->crypto->decrypt($privKey);
}
$decPayload = base64_decode($payload);
$rsa = new RSA();
$rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
@ -120,6 +128,9 @@ class ConfigController extends Controller {
if (is_array($payloadArray) && $configNonce !== '' && $configNonce === $payloadArray['nonce']) {
if (isset($payloadArray['key'])) {
$accessToken = $payloadArray['key'];
if ($accessToken !== '') {
$accessToken = $this->crypto->encrypt($accessToken);
}
$this->config->setUserValue($this->userId, Application::APP_ID, 'token', $accessToken);
// get user info
$url = $this->config->getUserValue($this->userId, Application::APP_ID, 'url');

Просмотреть файл

@ -15,6 +15,7 @@ use OCP\AppFramework\Controller;
use OCA\Discourse\Service\DiscourseAPIService;
use OCA\Discourse\AppInfo\Application;
use OCP\Security\ICrypto;
class DiscourseAPIController extends Controller {
@ -23,14 +24,23 @@ class DiscourseAPIController extends Controller {
private string $discourseUrl;
private string $discourseUsername;
public function __construct(string $appName,
IRequest $request,
private IConfig $config,
private DiscourseAPIService $discourseAPIService,
private ?string $userId) {
public function __construct(
string $appName,
IRequest $request,
private IConfig $config,
ICrypto $crypto,
private DiscourseAPIService $discourseAPIService,
private ?string $userId
) {
parent::__construct($appName, $request);
$this->accessToken = $this->config->getUserValue($this->userId, Application::APP_ID, 'token');
if ($this->accessToken !== '') {
$this->accessToken = $crypto->decrypt($this->accessToken);
}
$this->clientID = $this->config->getUserValue($this->userId, Application::APP_ID, 'client_id');
if ($this->clientID !== '') {
$this->clientID = $crypto->decrypt($this->clientID);
}
$this->discourseUrl = $this->config->getUserValue($this->userId, Application::APP_ID, 'url');
$this->discourseUsername = $this->config->getUserValue($this->userId, Application::APP_ID, 'user_name');
}

Просмотреть файл

@ -19,14 +19,18 @@ use OCP\Search\IProvider;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
use OCP\Search\SearchResultEntry;
use OCP\Security\ICrypto;
class DiscourseSearchPostsProvider implements IProvider {
public function __construct(private IAppManager $appManager,
private IL10N $l10n,
private IConfig $config,
private IURLGenerator $urlGenerator,
private DiscourseAPIService $discourseAPIService) {
public function __construct(
private IAppManager $appManager,
private IL10N $l10n,
private IConfig $config,
private ICrypto $crypto,
private IURLGenerator $urlGenerator,
private DiscourseAPIService $discourseAPIService
) {
}
/**
@ -75,6 +79,9 @@ class DiscourseSearchPostsProvider implements IProvider {
$discourseUrl = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'url');
$accessToken = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'token');
if ($accessToken !== '') {
$accessToken = $this->crypto->decrypt($accessToken);
}
$searchPostsEnabled = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'search_posts_enabled', '0') === '1';
if ($accessToken === '' || !$searchPostsEnabled) {

Просмотреть файл

@ -19,14 +19,18 @@ use OCP\Search\IProvider;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
use OCP\Search\SearchResultEntry;
use OCP\Security\ICrypto;
class DiscourseSearchTopicsProvider implements IProvider {
public function __construct(private IAppManager $appManager,
private IL10N $l10n,
private IConfig $config,
private IURLGenerator $urlGenerator,
private DiscourseAPIService $discourseAPIService) {
public function __construct(
private IAppManager $appManager,
private IL10N $l10n,
private IConfig $config,
private ICrypto $crypto,
private IURLGenerator $urlGenerator,
private DiscourseAPIService $discourseAPIService
) {
}
/**
@ -70,6 +74,9 @@ class DiscourseSearchTopicsProvider implements IProvider {
$discourseUrl = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'url');
$accessToken = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'token');
if ($accessToken !== '') {
$accessToken = $this->crypto->decrypt($accessToken);
}
$searchTopicsEnabled = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'search_topics_enabled', '0') === '1';
if ($accessToken === '' || !$searchTopicsEnabled) {

Просмотреть файл

@ -10,6 +10,7 @@ use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\IConfig;
use OCP\PreConditionNotMetException;
use OCP\Security\ICrypto;
use OCP\Settings\ISettings;
use OCA\Discourse\AppInfo\Application;
@ -19,9 +20,12 @@ use phpseclib\Crypt\RSA;
class Personal implements ISettings {
public function __construct(private IConfig $config,
private IInitialState $initialStateService,
private ?string $userId) {
public function __construct(
private IConfig $config,
private ICrypto $crypto,
private IInitialState $initialStateService,
private ?string $userId
) {
}
/**
@ -30,6 +34,9 @@ class Personal implements ISettings {
*/
public function getForm(): TemplateResponse {
$token = $this->config->getUserValue($this->userId, Application::APP_ID, 'token');
if ($token !== '') {
$token = $this->crypto->decrypt($token);
}
$url = $this->config->getUserValue($this->userId, Application::APP_ID, 'url');
$userName = $this->config->getUserValue($this->userId, Application::APP_ID, 'user_name');
$navigationEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'navigation_enabled', '0');
@ -38,13 +45,19 @@ class Personal implements ISettings {
// for OAuth
$clientID = $this->config->getUserValue($this->userId, Application::APP_ID, 'client_id');
if ($clientID !== '') {
$clientID = $this->crypto->decrypt($clientID);
}
$pubKey = $this->config->getAppValue(Application::APP_ID, 'public_key');
$privKey = $this->config->getAppValue(Application::APP_ID, 'private_key');
if ($privKey !== '') {
$privKey = $this->crypto->decrypt($privKey);
}
if ($clientID === '') {
// random string of 32 chars length
$permitted_chars = '0123456789abcdefghijklmnopqrstuvwxyz';
$clientID = substr(str_shuffle($permitted_chars), 0, 32);
$clientID = $this->crypto->encrypt(substr(str_shuffle($permitted_chars), 0, 32));
$this->config->setUserValue($this->userId, Application::APP_ID, 'client_id', $clientID);
}
if ($pubKey === '' || $privKey === '') {
@ -53,7 +66,7 @@ class Personal implements ISettings {
$rsa->setPublicKeyFormat(RSA::PUBLIC_FORMAT_PKCS1);
$keys = $rsa->createKey(2048);
$pubKey = $keys['publickey'];
$privKey = $keys['privatekey'];
$privKey = $this->crypto->encrypt($keys['privatekey']);
$this->config->setAppValue(Application::APP_ID, 'public_key', $pubKey);
$this->config->setAppValue(Application::APP_ID, 'private_key', $privKey);