From 5ad27ef896638b9342affb49e5d1b91863cdbf43 Mon Sep 17 00:00:00 2001 From: Xavier Jouslin de Noray <xavier.jouslindenoray@savoirfairelinux.com> Date: Mon, 14 Aug 2023 16:51:09 -0400 Subject: [PATCH] CRL: add crl manager for plugin store Change-Id: I883c5819228ab0bab88335e1dcbacfd25341a878 --- src/controllers/plugins.controller.ts | 54 ++++++++++++++++++- ...ager.ts => certificate.manager.service.ts} | 40 +++++++++++++- src/services/file.manager.service.ts | 9 +++- src/services/plugins.manager.service.ts | 4 +- tests/certificate.manager.test.ts | 39 +++++++++++--- tests/plugins.controller.test.ts | 8 ++- tests/plugins.manager.test.ts | 6 +-- 7 files changed, 142 insertions(+), 18 deletions(-) rename src/services/{certificate_manager.ts => certificate.manager.service.ts} (58%) diff --git a/src/controllers/plugins.controller.ts b/src/controllers/plugins.controller.ts index 2a8354e..6586199 100644 --- a/src/controllers/plugins.controller.ts +++ b/src/controllers/plugins.controller.ts @@ -25,6 +25,7 @@ import {StatusCodes} from 'http-status-codes'; import {IconsService} from '../services/icons.service'; import {BackgroundsService} from '../services/backgrounds.service'; import {TranslationService} from '../services/translation.service'; +import {CertificateManager} from '../services/certificate.manager.service'; @Service() export class PluginsController { @@ -34,7 +35,8 @@ export class PluginsController { private readonly pluginsManager: PluginsManager, private readonly translation: TranslationService, private readonly iconsManager: IconsService, - private readonly backgroundsManager: BackgroundsService + private readonly backgroundsManager: BackgroundsService, + private readonly certificateManager: CertificateManager ) { this.configureRouter(); } @@ -414,6 +416,56 @@ export class PluginsController { res.status(StatusCodes.NOT_FOUND).send(); } }); + /** + * @swagger + * + * /crl?{org}: + * get: + * summary: Get the crl + * description: Returns the corresponding crl for the query. + * parameters: + * - name: org + * in: query + * description: the query crl. + * required: true + * schema: + * type: string + * responses: + * '200': + * description: Successful response the crl. + * content: + * application/json: + * schema: + * type: string + * '404': + * description: Not found - When the query crl is not found. + * '500': + * description: Internal server error - Something went wrong on the server. + */ + // eslint-disable-next-line @typescript-eslint/no-misused-promises + this.router.get('/crl/:org', async (req: Request, res: Response) => { + try { + let org = req.params.org; + if (org === undefined) { + org = 'root'; + } + if (org !== 'root' && org !== 'sfl') { + res.status(StatusCodes.BAD_REQUEST).send(); + return; + } + const crl = + org === 'root' + ? await this.certificateManager.getCaCrlPath() + : await this.certificateManager.getSflCrlPath(); + if (crl === '') { + res.status(StatusCodes.NOT_FOUND).send(); + return; + } + res.status(StatusCodes.OK).download(crl); + } catch (e) { + res.status(StatusCodes.INTERNAL_SERVER_ERROR).send(); + } + }); } } diff --git a/src/services/certificate_manager.ts b/src/services/certificate.manager.service.ts similarity index 58% rename from src/services/certificate_manager.ts rename to src/services/certificate.manager.service.ts index de8dca2..c3b956d 100644 --- a/src/services/certificate_manager.ts +++ b/src/services/certificate.manager.service.ts @@ -20,7 +20,7 @@ import {FileManagerService} from './file.manager.service'; import {X509Certificate} from 'crypto'; @Service() -export class CertificateManagerService { +export class CertificateManager { constructor(private readonly fileManager: FileManagerService) {} async getIssuer(path: string, file: string): Promise<Record<string, string>> { @@ -28,6 +28,40 @@ export class CertificateManagerService { return this.parseDN(certificate.issuer); } + async getCaCrlPath(): Promise<string> { + const crlFilePath = + (process.env.DATA_DIRECTORY as string) + '/' + 'root.crl'; + const isCRLFileExist = await this.fileManager.isFileExist(crlFilePath); + if (process.env.DATA_DIRECTORY === undefined || !isCRLFileExist) { + return ''; + } + return crlFilePath; + } + + async getSflCrlPath(): Promise<string> { + const crlFilePath = + (process.env.DATA_DIRECTORY as string) + '/' + 'SFL.crl'; + const isCRLFileExist = await this.fileManager.isFileExist(crlFilePath); + if (process.env.DATA_DIRECTORY === undefined || !isCRLFileExist) { + return ''; + } + return crlFilePath; + } + + async getCaCrl(): Promise<string> { + if (process.env.DATA_DIRECTORY === undefined) { + return ''; + } + return await this.readCRL(process.env.DATA_DIRECTORY + '/' + 'ca.crl'); + } + + async getSflCrl(): Promise<string> { + if (process.env.DATA_DIRECTORY === undefined) { + return ''; + } + return await this.readCRL(process.env.DATA_DIRECTORY + '/' + 'sfl.crl'); + } + private async readCertificate( path: string, file: string @@ -39,6 +73,10 @@ export class CertificateManagerService { }); } + private async readCRL(file: string): Promise<string> { + return (await this.fileManager.readFile(file)).toString(); + } + parseDN(dn: string): Record<string, string> { return dn .split('\n') diff --git a/src/services/file.manager.service.ts b/src/services/file.manager.service.ts index 1563492..a42f9b6 100644 --- a/src/services/file.manager.service.ts +++ b/src/services/file.manager.service.ts @@ -15,7 +15,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -import {type Stats, promises, watch, type FSWatcher} from 'fs'; +import {type Stats, promises, watch, type FSWatcher, constants} from 'fs'; import {Service} from 'typedi'; import StreamZip from 'node-stream-zip'; @Service() @@ -28,6 +28,13 @@ export class FileManagerService { } } + async isFileExist(path: string): Promise<boolean> { + return await promises + .access(path, constants.F_OK) + .then(() => true) + .catch(() => false); + } + async listFiles(path: string): Promise<string[]> { try { // check if the path is a directory diff --git a/src/services/plugins.manager.service.ts b/src/services/plugins.manager.service.ts index df7a8a2..fd111a8 100644 --- a/src/services/plugins.manager.service.ts +++ b/src/services/plugins.manager.service.ts @@ -20,7 +20,7 @@ import {Service} from 'typedi'; import {FileManagerService} from './file.manager.service'; import {type Plugins} from '../interfaces/plugins'; import {TranslationService} from './translation.service'; -import {CertificateManagerService} from './certificate_manager'; +import {CertificateManager} from './certificate.manager.service'; import {ArchitectureManager} from './architecture.manager'; import {basename, extname} from 'path'; @@ -31,7 +31,7 @@ export class PluginsManager { private readonly architectureManager: ArchitectureManager, private readonly translation: TranslationService, private readonly fileManager: FileManagerService, - private readonly certificateManager: CertificateManagerService + private readonly certificateManager: CertificateManager ) { if (process.env.DATA_DIRECTORY === undefined) { return; diff --git a/tests/certificate.manager.test.ts b/tests/certificate.manager.test.ts index 125f485..2453569 100644 --- a/tests/certificate.manager.test.ts +++ b/tests/certificate.manager.test.ts @@ -21,15 +21,16 @@ import 'module-alias/register'; import { promises } from 'fs'; import Sinon, { createStubInstance, restore, stub } from 'sinon'; import { FileManagerService } from '../src/services/file.manager.service'; -import { CertificateManagerService } from '../src/services/certificate_manager'; +import { CertificateManager } from '../src/services/certificate.manager.service'; describe('Certificate manager service tests', function () { let fileManager: Sinon.SinonStubbedInstance<FileManagerService>; - let certificateManagerService: CertificateManagerService; + let certificateManager: CertificateManager; beforeEach(() => { + process.env.DATA_DIRECTORY = '/path/to/plugins'; fileManager = createStubInstance(FileManagerService); - certificateManagerService = new CertificateManagerService(fileManager); + certificateManager = new CertificateManager(fileManager); }); afterEach(() => { @@ -42,10 +43,10 @@ describe('Certificate manager service tests', function () { const fakeCertificate = { CN: 'test' }; const expectedIssuer = { CN: 'test' }; - stub(Object.getPrototypeOf(certificateManagerService), 'readCertificate').resolves(fakeCertificate); - stub(certificateManagerService, 'parseDN').returns(expectedIssuer); + stub(Object.getPrototypeOf(certificateManager), 'readCertificate').resolves(fakeCertificate); + stub(certificateManager, 'parseDN').returns(expectedIssuer); - const actual = await certificateManagerService.getIssuer(path, file); + const actual = await certificateManager.getIssuer(path, file); expect(actual).toEqual(expectedIssuer); }); @@ -58,11 +59,33 @@ describe('Certificate manager service tests', function () { fileManager.readArchive.resolves(expectedCertificate); - const actual = await certificateManagerService['readCertificate'](path, file); + const actual = await certificateManager['readCertificate'](path, file); expect(actual.subject).toEqual(expectedSubject); }); + it('should return CRL', async () => { + const path = '/path/to/certificates'; + const file = 'ca.crl'; + const expectedCRL = 'test'; + fileManager.readFile.resolves(Buffer.from(expectedCRL, 'utf-8')); + const crl = await certificateManager['readCRL'](path + '/' + file); + expect(crl).toEqual(expectedCRL); + }); + + it('should return sfl crl', async () => { + const expectedCRL = 'test'; + fileManager.readFile.resolves(Buffer.from(expectedCRL, 'utf-8')); + const crl = await certificateManager.getSflCrl(); + expect(crl).toEqual(expectedCRL); + }); + + it('should return ca crl', async () => { + const expectedCRL = 'test'; + fileManager.readFile.resolves(Buffer.from(expectedCRL, 'utf-8')); + const crl = await certificateManager.getCaCrl(); + expect(crl).toEqual(expectedCRL); + }); it('should parse the DN correctly', () => { const dn = 'CN=John Doe\nO=Acme Corp'; @@ -71,7 +94,7 @@ describe('Certificate manager service tests', function () { O: 'Acme Corp' }; - const actual = certificateManagerService.parseDN(dn); + const actual = certificateManager.parseDN(dn); expect(actual).toEqual(expected); }); diff --git a/tests/plugins.controller.test.ts b/tests/plugins.controller.test.ts index 328146a..4e58abf 100644 --- a/tests/plugins.controller.test.ts +++ b/tests/plugins.controller.test.ts @@ -29,6 +29,8 @@ import fs from "fs"; import { TranslationService } from '../src/services/translation.service'; import { IconsService } from '../src/services/icons.service'; import { BackgroundsService } from '../src/services/backgrounds.service'; +import { CertificateManager } from '../src/services/certificate.manager.service'; + describe('Routes', function () { @@ -36,6 +38,7 @@ describe('Routes', function () { let transaltionStub: SinonStubbedInstance<TranslationService>; let iconsManagerStub: SinonStubbedInstance<IconsService>; let backgroundsService: SinonStubbedInstance<BackgroundsService>; + let certificateManager: SinonStubbedInstance<CertificateManager>; let expressApp: Express.Application; beforeEach(() => { @@ -43,12 +46,13 @@ describe('Routes', function () { transaltionStub = createStubInstance(TranslationService); iconsManagerStub = createStubInstance(IconsService); backgroundsService = createStubInstance(BackgroundsService); - + certificateManager = createStubInstance(CertificateManager); const app = Container.get(Application); Object.defineProperty(app['pluginsController'], 'pluginsManager', { value: pluginsManagerServiceStub }); Object.defineProperty(app['pluginsController'], 'translation', { value: transaltionStub }); Object.defineProperty(app['pluginsController'], 'iconsManager', { value: iconsManagerStub }); - Object.defineProperty(app['pluginsController'], 'backgroundsManager', { value: BackgroundsService }); + Object.defineProperty(app['pluginsController'], 'backgroundsManager', { value: backgroundsService }); + Object.defineProperty(app['pluginsController'], 'certificateManager', { value: certificateManager }); expressApp = app.app; }); diff --git a/tests/plugins.manager.test.ts b/tests/plugins.manager.test.ts index c611b99..8088280 100644 --- a/tests/plugins.manager.test.ts +++ b/tests/plugins.manager.test.ts @@ -23,7 +23,7 @@ import { Stats } from 'fs'; import { PluginsManager } from "../src/services/plugins.manager.service"; import { FileManagerService } from '../src/services/file.manager.service'; -import { CertificateManagerService } from '../src/services/certificate_manager'; +import { CertificateManager} from '../src/services/certificate.manager.service'; import { ArchitectureManager } from '../src/services/architecture.manager'; import { TranslationService } from '../src/services/translation.service'; @@ -32,11 +32,11 @@ describe('Plugins manager service tests', function () { let architectureService: Sinon.SinonStubbedInstance<ArchitectureManager>; let transaltionStub: Sinon.SinonStubbedInstance<TranslationService>; let fileManager: Sinon.SinonStubbedInstance<FileManagerService>; - let certificateManager: Sinon.SinonStubbedInstance<CertificateManagerService>; + let certificateManager: Sinon.SinonStubbedInstance<CertificateManager>; beforeEach(() => { fileManager = createStubInstance(FileManagerService); - certificateManager = createStubInstance(CertificateManagerService); + certificateManager = createStubInstance(CertificateManager); architectureService = createStubInstance(ArchitectureManager); transaltionStub = createStubInstance(TranslationService); pluginsManagerService = new PluginsManager(architectureService, transaltionStub, fileManager, certificateManager); -- GitLab