From 52c6b351766a8464c9c6bbc96eb23a6cf2d03138 Mon Sep 17 00:00:00 2001 From: Xavier Jouslin de Noray <xavier.jouslindenoray@savoirfairelinux.com> Date: Tue, 12 Dec 2023 09:05:01 -0500 Subject: [PATCH] Upload Plugin: check if the plugin can be uploadable Gitlab: #10 Change-Id: I9b5d44179c32a0d2bf4c1ddeaf2fb9e0fde059da --- src/controllers/plugins.controller.ts | 61 +++++++++++++++++++++ src/services/certificate.manager.service.ts | 17 ++++-- src/services/plugins.manager.service.ts | 22 ++++++++ 3 files changed, 96 insertions(+), 4 deletions(-) diff --git a/src/controllers/plugins.controller.ts b/src/controllers/plugins.controller.ts index 6586199..0368283 100644 --- a/src/controllers/plugins.controller.ts +++ b/src/controllers/plugins.controller.ts @@ -247,6 +247,67 @@ export class PluginsController { } } ); + /** + * @swagger + * + * /upload/{arch}/{id}: + * get: + * summary: Verify if the plugin is already uploaded with the same issuer + * description: Verify if the plugin is already uploaded with the same issuer + * tags: [Example, Time] + * parameters: + * - name: id + * in: path + * description: The ID of the plugin to be downloaded. + * required: true + * schema: + * type: string + * - name: arch + * in: path + * description: The architecture for which the plugin is requested. + * required: true + * schema: + * type: string + * responses: + * '200': + * description: The plugin is uploadable. + * '400': + * description: Bad request - When the 'id', 'arch' parameter or the 'Authorization' http header is missing. + * '403': + * description: Forbidden - When the plugin is already uploaded with a different issuer. + * '500': + * description: Internal server error - Something went wrong on the server. + * + */ + + // GET /upload/:arch/:id + this.router.get('/upload/:arch/:id', (req: Request, res: Response) => { + try { + const clientCertificate = req.get('Authorization'); + if ( + req.params.id === undefined || + req.params.arch === undefined || + clientCertificate === undefined + ) { + res.status(StatusCodes.BAD_REQUEST).send(); + return; + } + this.pluginsManager + .isPluginUploadable( + req.params.id, + req.params.arch, + Buffer.from(clientCertificate, 'base64') + ) + .then(isUploadable => + res + .status(isUploadable ? StatusCodes.OK : StatusCodes.FORBIDDEN) + .send() + ) + .catch(() => res.status(StatusCodes.INTERNAL_SERVER_ERROR).send()); + } catch (e) { + res.status(StatusCodes.INTERNAL_SERVER_ERROR).send(); + } + }); /** * @swagger diff --git a/src/services/certificate.manager.service.ts b/src/services/certificate.manager.service.ts index 20eb1f8..5b402b5 100644 --- a/src/services/certificate.manager.service.ts +++ b/src/services/certificate.manager.service.ts @@ -66,10 +66,7 @@ export class CertificateManager { return Buffer.compare(signature, checkSignature) === 0; } - private async readCertificate( - path: string, - file: string - ): Promise<X509Certificate> { + async readCertificate(path: string, file: string): Promise<X509Certificate> { return await this.fileManager .readArchive(path, file) .then((content: Buffer) => { @@ -90,4 +87,16 @@ export class CertificateManager { return acc; }, {}); } + + isSameKey(issuer: X509Certificate, plugin: X509Certificate): boolean { + const key1Formatted = issuer.publicKey.export({ + format: 'pem', + type: 'pkcs1', + }); + const key2Formatted = plugin.publicKey.export({ + format: 'pem', + type: 'pkcs1', + }); + return key1Formatted === key2Formatted; + } } diff --git a/src/services/plugins.manager.service.ts b/src/services/plugins.manager.service.ts index 18a5414..bf96f84 100644 --- a/src/services/plugins.manager.service.ts +++ b/src/services/plugins.manager.service.ts @@ -23,6 +23,7 @@ import {TranslationService} from './translation.service'; import {CertificateManager} from './certificate.manager.service'; import {ArchitectureManager} from './architecture.manager'; import {basename, extname} from 'path'; +import {X509Certificate} from 'crypto'; @Service() export class PluginsManager { @@ -253,6 +254,27 @@ export class PluginsManager { }; } + async isPluginUploadable( + id: string, + arch: string, + requestCertificate: Buffer + ): Promise<boolean> { + const requestedCertificate = new X509Certificate(requestCertificate); + const remotePlugin = await this.findPlugin(id, arch); + const remotePluginPath = await this.getPluginPath(id, arch); + if (remotePlugin === undefined || remotePluginPath === undefined) { + return true; + } + const remoteCertificate = await this.certificateManager.readCertificate( + remotePluginPath, + id + '.crt' + ); + return this.certificateManager.isSameKey( + requestedCertificate, + remoteCertificate + ); + } + async getVersions(): Promise<Array<{id: string; version: string}>> { if (this.plugins.length === 0) { await this.setPlugins(); -- GitLab