[OP#45466]Remove expired direct upload tokens using background jobs (#341)

* Added function to remove all expired tokens for direct upload

Signed-off-by: sagargurung1001@gmail.com <sagargurung1001@gmail.com>

* use time() as timestamp

Signed-off-by: sagargurung1001@gmail.com <sagargurung1001@gmail.com>

* Added unit tests for delete upload tokens

Signed-off-by: sagargurung1001@gmail.com <sagargurung1001@gmail.com>

* fix lint

Signed-off-by: sagargurung1001@gmail.com <sagargurung1001@gmail.com>

* CI check to enable integration app

Signed-off-by: sagargurung1001@gmail.com <sagargurung1001@gmail.com>

* Follow up CI failure

Signed-off-by: sagargurung1001@gmail.com <sagargurung1001@gmail.com>

---------

Signed-off-by: sagargurung1001@gmail.com <sagargurung1001@gmail.com>
This commit is contained in:
Sagar Gurung 2023-01-30 12:27:40 +05:45 коммит произвёл GitHub
Родитель 3e72773e45
Коммит b5aaa10af6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 279 добавлений и 8 удалений

18
.github/workflows/ci.yml поставляемый
Просмотреть файл

@ -91,6 +91,11 @@ jobs:
- name: PHP & Vue Unit Tests
run: |
mkdir -p server/apps/integration_openproject
cp -r `ls -A | grep -v 'server'` server/apps/integration_openproject/
cd server
./occ a:e integration_openproject
cd apps/integration_openproject
make phpunit
make jsunit
@ -99,7 +104,7 @@ jobs:
uses: romeovs/lcov-reporter-action@v0.3.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
lcov-file: ./coverage/jest/lcov.info
lcov-file: ./server/apps/integration_openproject/coverage/jest/lcov.info
delete-old-comments: true
title: "JS Code Coverage"
@ -107,8 +112,8 @@ jobs:
if: ${{ github.event_name == 'pull_request' && matrix.nextcloudVersion == 'master' && matrix.phpVersion == '8.1' }}
uses: danielpalme/ReportGenerator-GitHub-Action@5.0.3
with:
reports: './coverage/php/cobertura.xml' # REQUIRED # The coverage reports that should be parsed (separated by semicolon). Globbing is supported.
targetdir: './coverage/php' # REQUIRED # The directory where the generated report should be saved.
reports: './server/apps/integration_openproject/coverage/php/cobertura.xml' # REQUIRED # The coverage reports that should be parsed (separated by semicolon). Globbing is supported.
targetdir: './server/apps/integration_openproject/coverage/php' # REQUIRED # The directory where the generated report should be saved.
reporttypes: 'lcov' # The output formats and scope (separated by semicolon) Values: Badges, Clover, Cobertura, CsvSummary, Html, HtmlChart, HtmlInline, HtmlInline_AzurePipelines, HtmlInline_AzurePipelines_Dark, HtmlSummary, JsonSummary, Latex, LatexSummary, lcov, MarkdownSummary, MHtml, PngChart, SonarQube, TeamCitySummary, TextSummary, Xml, XmlSummary
sourcedirs: '' # Optional directories which contain the corresponding source code (separated by semicolon). The source directories are used if coverage report contains classes without path information.
historydir: '' # Optional directory for storing persistent coverage information. Can be used in future reports to show coverage evolution.
@ -127,7 +132,7 @@ jobs:
uses: romeovs/lcov-reporter-action@v0.3.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
lcov-file: ./coverage/php/lcov.info
lcov-file: ./server/apps/integration_openproject/coverage/php/lcov.info
delete-old-comments: true
title: "PHP Code Coverage"
@ -136,14 +141,15 @@ jobs:
uses: VeryGoodOpenSource/very_good_coverage@v1.2.0
with:
min_coverage: '59'
path: './coverage/jest/lcov.info'
path: './server/apps/integration_openproject/coverage/jest/lcov.info'
- name: PHP coverage check
if: ${{ github.event_name == 'pull_request' && matrix.nextcloudVersion == 'master' && matrix.phpVersion == '8.1' }}
uses: VeryGoodOpenSource/very_good_coverage@v1.2.0
with:
min_coverage: '57'
path: './coverage/php/lcov.info'
path: './server/apps/integration_openproject/coverage/php/lcov.info'
api-tests:
name: API tests
strategy:

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

@ -38,7 +38,7 @@ For more information on how to set up and use the OpenProject application, pleas
<nextcloud min-version="22" max-version="26"/>
</dependencies>
<background-jobs>
<job>OCA\OpenProject\BackgroundJob\CheckNotifications</job>
<job>OCA\OpenProject\BackgroundJob\RemoveExpiredDirectUploadTokens</job>
</background-jobs>
<settings>
<admin>OCA\OpenProject\Settings\Admin</admin>

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

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2023 Sagar Gurung <sagar@jankaritech.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\OpenProject\BackgroundJob;
use OCP\BackgroundJob\TimedJob;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\Exception;
use Psr\Log\LoggerInterface;
use OCA\OpenProject\Service\DatabaseService;
class RemoveExpiredDirectUploadTokens extends TimedJob {
/** @var LoggerInterface */
protected LoggerInterface $logger;
/** @var DatabaseService */
protected DatabaseService $databaseService;
public function __construct(ITimeFactory $time, DatabaseService $databaseService, LoggerInterface $logger) {
parent::__construct($time);
// runs once a day
$this->setInterval(24 * 3600);
$this->databaseService = $databaseService;
$this->logger = $logger;
}
/**
* @param mixed $argument
* @return void
* @throws Exception
*/
public function run($argument): void {
$this->databaseService->deleteExpiredTokens();
$this->logger->info('Deleted all the expired tokens from Database');
}
}

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

@ -31,7 +31,7 @@ class DatabaseService {
/**
* @var IDBConnection
*/
private IDBConnection $db;
public IDBConnection $db;
/** @var string table name */
@ -102,6 +102,19 @@ class DatabaseService {
];
}
/**
*
* @throws Exception
*/
public function deleteExpiredTokens(): void {
$query = $this->db->getQueryBuilder();
$query->delete($this->table)
->where(
$query->expr()->lt('expires_on', $query->createNamedParameter(time()))
);
$query->execute();
}
/**
* deletes the token from the table
* @param string $token

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

@ -0,0 +1,194 @@
<?php
/**
* @copyright Copyright (c) 2022 Swikriti Tripathi <swikriti@jankaritech.com>
*
* @author Your name <swikriti@jankaritech.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\OpenProject\Service;
use OCA\OpenProject\AppInfo\Application;
use OCP\DB\Exception;
use PHPUnit\Framework\TestCase;
class DatabaseServiceTest extends TestCase {
/**
* @var DatabaseService
*/
private $databaseService;
private const TABLE_NAME = 'direct_upload';
/**
* createdAt and expiresOn info is not included since it requires current timestamp
*
* @var array <mixed>
*/
private array $unexpiredDirectUploadInfo = [
[
"token" => 'unExpiredToken1',
"folderId" => 1,
"userId" => 'u1',
],
[
"token" => 'unExpiredToken2',
"folderId" => 1,
"userId" => 'u1',
],
[
"token" => 'unExpiredToken3',
"folderId" => 1,
"userId" => 'u1',
],
];
/**
* createdAt and expiresOn info is not included since it requires current timestamp
*
* @var array <mixed>
*/
private array $expiredDirectUploadInfo = [
[
"token" => 'expiredToken1',
"folderId" => 1,
"userId" => 'u1',
],
[
"token" => 'expiredToken2',
"folderId" => 1,
"userId" => 'u1',
],
[
"token" => 'expiredToken3',
"folderId" => 1,
"userId" => 'u1',
],
];
protected function setUp(): void {
$app = new Application();
$c = $app->getContainer();
/** @var DatabaseService $databaseService */
$databaseService = $c->get(DatabaseService::class);
$this->databaseService = $databaseService;
}
/**
* @throws Exception
*/
protected function tearDown(): void {
$query = $this->databaseService->db->getQueryBuilder();
$query->delete(self::TABLE_NAME);
$query->execute();
}
/**
* @throws Exception
* @return array<mixed>
*/
public function getAllTokensFromTable(): array {
$tokens = [];
$query = $this->databaseService->db->getQueryBuilder();
$query->select('token')
->from(self::TABLE_NAME);
$req = $query->executeQuery();
while ($row = $req->fetch()) {
$tokens[] = $row['token'];
}
return $tokens;
}
/**
* @return void
*
* @throws Exception
*/
public function testDeleteSingleExpiredToken(): void {
$this->databaseService->setInfoForDirectUpload("expiredToken", 1, "u1", time(), time() - 1000);
$this->databaseService->deleteExpiredTokens();
$token = $this->getAllTokensFromTable();
self::assertEquals(0, sizeof($token));
}
/**
* @return void
*
* @throws Exception
*/
public function testDeleteMultipleExpiredTokens(): void {
foreach ($this->expiredDirectUploadInfo as $info) {
$this->databaseService->setInfoForDirectUpload($info['token'], $info['folderId'], $info['userId'], time(), time() - 1000);
}
$this->databaseService->deleteExpiredTokens();
$tokens = $this->getAllTokensFromTable();
self::assertEquals(0, sizeof($tokens));
}
/**
* @return void
*
* @throws Exception
*/
public function testDeleteSingleUnExpiredToken(): void {
$this->databaseService->setInfoForDirectUpload("unExpiredToken", 1, "u1", time(), time() + 1000);
$this->databaseService->deleteExpiredTokens();
$token = $this->getAllTokensFromTable();
self::assertSame('unExpiredToken', $token[0]);
}
/**
* @return void
*
* @throws Exception
*/
public function testDeleteMultipleUnExpiredTokens(): void {
foreach ($this->unexpiredDirectUploadInfo as $info) {
$this->databaseService->setInfoForDirectUpload($info['token'], $info['folderId'], $info['userId'], time(), time() + 1000);
}
$this->databaseService->deleteExpiredTokens();
$tokens = $this->getAllTokensFromTable();
self::assertEquals(sizeof($this->unexpiredDirectUploadInfo), sizeof($tokens));
foreach ($this->unexpiredDirectUploadInfo as $info) {
self::assertContains($info['token'], $tokens);
}
}
/**
* @return void
*
* @throws Exception
*/
public function testDeleteMultipleExpiredAndUnexpiredTokens(): void {
foreach ($this->expiredDirectUploadInfo as $info) {
$this->databaseService->setInfoForDirectUpload($info['token'], $info['folderId'], $info['userId'], time(), time() - 1000);
}
foreach ($this->unexpiredDirectUploadInfo as $info) {
$this->databaseService->setInfoForDirectUpload($info['token'], $info['folderId'], $info['userId'], time(), time() + 1000);
}
$this->databaseService->deleteExpiredTokens();
$tokens = $this->getAllTokensFromTable();
self::assertEquals(sizeof($this->unexpiredDirectUploadInfo), sizeof($tokens));
foreach ($this->unexpiredDirectUploadInfo as $info) {
self::assertContains($info['token'], $tokens);
}
}
}