From c5ac715b2b48af8a353a0374631f35f46c66a740 Mon Sep 17 00:00:00 2001 From: hezhengxu2018 <35598090+hezhengxu2018@users.noreply.github.com> Date: Thu, 6 Apr 2023 12:24:41 +0800 Subject: [PATCH] feat: allow admin to sync package only (#434) closes #412 --- app/common/constants.ts | 1 + app/port/controller/AbstractController.ts | 7 ++-- app/port/controller/PackageSyncController.ts | 5 +++ .../schedule/CheckRecentlyUpdatedPackages.ts | 4 ++- app/port/schedule/SyncPackageWorker.ts | 3 +- config/config.default.ts | 3 +- .../createSyncTask.test.ts | 34 +++++++++++++++++++ 7 files changed, 51 insertions(+), 6 deletions(-) diff --git a/app/common/constants.ts b/app/common/constants.ts index 9dc834a7..01236385 100644 --- a/app/common/constants.ts +++ b/app/common/constants.ts @@ -3,6 +3,7 @@ export const LATEST_TAG = 'latest'; export const GLOBAL_WORKER = 'GLOBAL_WORKER'; export enum SyncMode { none = 'none', + admin = 'admin', exist = 'exist', all = 'all', } diff --git a/app/port/controller/AbstractController.ts b/app/port/controller/AbstractController.ts index 9d16713c..1a34755f 100644 --- a/app/port/controller/AbstractController.ts +++ b/app/port/controller/AbstractController.ts @@ -21,6 +21,7 @@ import { UserService } from '../../core/service/UserService'; import { VersionRule, } from '../typebox'; +import { SyncMode } from '../../common/constants'; class PackageNotFoundError extends NotFoundError {} @@ -43,7 +44,7 @@ export abstract class AbstractController extends MiddlewareController { } protected get enableSync() { - return this.config.cnpmcore.syncMode === 'all' || this.config.cnpmcore.syncMode === 'exist'; + return this.config.cnpmcore.syncMode !== SyncMode.none; } protected isPrivateScope(scope: string) { @@ -104,8 +105,8 @@ export abstract class AbstractController extends MiddlewareController { const [ scope ] = getScopeAndName(fullname); // dont sync private scope if (!this.isPrivateScope(scope)) { - // syncMode = none, redirect public package to source registry - if (!this.enableSync) { + // syncMode = none/admin, redirect public package to source registry + if (!this.enableSync && this.config.cnpmcore.syncMode !== SyncMode.admin) { if (this.redirectNotFound) { err.redirectToSourceRegistry = this.sourceRegistry; } diff --git a/app/port/controller/PackageSyncController.ts b/app/port/controller/PackageSyncController.ts index 541bca0e..a2d57679 100644 --- a/app/port/controller/PackageSyncController.ts +++ b/app/port/controller/PackageSyncController.ts @@ -18,6 +18,7 @@ import { PackageSyncerService } from '../../core/service/PackageSyncerService'; import { RegistryManagerService } from '../../core/service/RegistryManagerService'; import { TaskState } from '../../common/enum/Task'; import { SyncPackageTaskRule, SyncPackageTaskType } from '../typebox'; +import { SyncMode } from '../../common/constants'; @HTTPController() export class PackageSyncController extends AbstractController { @@ -60,6 +61,10 @@ export class PackageSyncController extends AbstractController { const tips = data.tips || `Sync cause by "${ctx.href}", parent traceId: ${ctx.tracer.traceId}`; const isAdmin = await this.userRoleManager.isAdmin(ctx); + if (this.config.cnpmcore.syncMode === SyncMode.admin && !isAdmin) { + throw new ForbiddenError('Only admin allow to sync package'); + } + const params = { fullname, tips, diff --git a/app/port/schedule/CheckRecentlyUpdatedPackages.ts b/app/port/schedule/CheckRecentlyUpdatedPackages.ts index a7da5511..721376b2 100644 --- a/app/port/schedule/CheckRecentlyUpdatedPackages.ts +++ b/app/port/schedule/CheckRecentlyUpdatedPackages.ts @@ -4,6 +4,7 @@ import { Inject } from '@eggjs/tegg'; import { PackageSyncerService } from '../../core/service/PackageSyncerService'; import { PackageRepository } from '../../repository/PackageRepository'; import { getScopeAndName } from '../../common/PackageUtil'; +import { SyncMode } from '../../common/constants'; // https://github.com/cnpm/cnpmcore/issues/9 @Schedule({ @@ -29,7 +30,8 @@ export class CheckRecentlyUpdatedPackages { private readonly httpclient: EggHttpClient; async subscribe() { - if (this.config.cnpmcore.syncMode === 'none' || !this.config.cnpmcore.enableCheckRecentlyUpdated) return; + const notAllowUpdateModeList = [ SyncMode.none, SyncMode.admin ]; + if (notAllowUpdateModeList.includes(this.config.cnpmcore.syncMode) || !this.config.cnpmcore.enableCheckRecentlyUpdated) return; const pageSize = 36; const pageCount = this.config.env === 'unittest' ? 2 : 5; for (let pageIndex = 0; pageIndex < pageCount; pageIndex++) { diff --git a/app/port/schedule/SyncPackageWorker.ts b/app/port/schedule/SyncPackageWorker.ts index 48bc605c..d8897bae 100644 --- a/app/port/schedule/SyncPackageWorker.ts +++ b/app/port/schedule/SyncPackageWorker.ts @@ -2,6 +2,7 @@ import { EggAppConfig, EggLogger } from 'egg'; import { IntervalParams, Schedule, ScheduleType } from '@eggjs/tegg/schedule'; import { Inject } from '@eggjs/tegg'; import { PackageSyncerService } from '../../core/service/PackageSyncerService'; +import { SyncMode } from '../../common/constants'; let executingCount = 0; @@ -23,7 +24,7 @@ export class SyncPackageWorker { private readonly logger: EggLogger; async subscribe() { - if (this.config.cnpmcore.syncMode === 'none') return; + if (this.config.cnpmcore.syncMode === SyncMode.none) return; if (executingCount >= this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks) return; executingCount++; diff --git a/config/config.default.ts b/config/config.default.ts index c192ddd4..0f7a8331 100644 --- a/config/config.default.ts +++ b/config/config.default.ts @@ -20,7 +20,8 @@ export default (appInfo: EggAppConfig) => { sourceRegistrySyncTimeout: 180000, taskQueueHighWaterSize: 100, // sync mode - // - none: don't sync npm package, just redirect it to sourceRegistry + // - none: don't sync npm package + // - admin: don't sync npm package,only admin can create sync task by sync contorller. // - all: sync all npm packages // - exist: only sync exist packages, effected when `enableCheckRecentlyUpdated` or `enableChangesStream` is enabled syncMode: SyncMode.none, diff --git a/test/port/controller/PackageSyncController/createSyncTask.test.ts b/test/port/controller/PackageSyncController/createSyncTask.test.ts index e61837f7..5eca6df5 100644 --- a/test/port/controller/PackageSyncController/createSyncTask.test.ts +++ b/test/port/controller/PackageSyncController/createSyncTask.test.ts @@ -21,6 +21,23 @@ describe('test/port/controller/PackageSyncController/createSyncTask.test.ts', () assert(res.body.error === '[FORBIDDEN] Not allow to sync package'); }); + it('should 403 when syncMode = admin', async () => { + mock(app.config.cnpmcore, 'syncMode', 'admin'); + const res = await app.httpRequest() + .put('/-/package/koa/syncs') + .expect(403); + assert(res.body.error === '[FORBIDDEN] Only admin allow to sync package'); + }); + + it('should 201 when syncMode = admin & login as admin', async () => { + mock(app.config.cnpmcore, 'syncMode', 'admin'); + const adminUser = await TestUtil.createAdmin(); + await app.httpRequest() + .put('/-/package/koa/syncs') + .set('authorization', adminUser.authorization) + .expect(201); + }); + it('should 401 if user not login when alwaysAuth = true', async () => { mock(app.config.cnpmcore, 'alwaysAuth', true); const res = await app.httpRequest() @@ -315,6 +332,23 @@ describe('test/port/controller/PackageSyncController/createSyncTask.test.ts', () assert(res.body.error === '[FORBIDDEN] Not allow to sync package'); }); + it('should 403 when syncMode = admin', async () => { + mock(app.config.cnpmcore, 'syncMode', 'admin'); + const res = await app.httpRequest() + .put('/koa/sync') + .expect(403); + assert(res.body.error === '[FORBIDDEN] Only admin allow to sync package'); + }); + + it('should 201 when syncMode = admin & login as admin', async () => { + mock(app.config.cnpmcore, 'syncMode', 'admin'); + const adminUser = await TestUtil.createAdmin(); + await app.httpRequest() + .put('/koa/sync') + .set('authorization', adminUser.authorization) + .expect(201); + }); + it('should 201', async () => { let res = await app.httpRequest() .put('/koa/sync')