diff --git a/src/controllers/plugins.controller.ts b/src/controllers/plugins.controller.ts index 2a8354e366ecb5277388144fc54a76616b9b3c6c..6586199dd5273b9f74931874a1dfce94a040ab27 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 de8dca25164532cc8a4a1b6f8d5d9b0013b33349..c3b956d15cf46325d3b8637f5b8539f67fd9386d 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 15634921690c5a9c8dcc5288aba60931393889e3..a42f9b6c34f889e005a73c4852257e02d8d979eb 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 df7a8a2ad470be5da286f9d70d9e16b4b97492a8..fd111a86cd0e3ef5889cccf4a955614db10e1cda 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 125f485122d45f9f17dca028582b4530732684c9..24535693593ca4e617f9248352f87c03571d96ae 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 328146a666d195fe784e5b50154d01047ed6a732..4e58abf8d844ac52d9fff8e66f1574b3424031da 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 c611b994c26f57b63f83bd90eb82920a1ba552e2..80882809549d04d96f49134940962e0eadbcbea8 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);