Upload Plugin: check if the plugin can be uploadable

Gitlab: #10
Change-Id: I9b5d44179c32a0d2bf4c1ddeaf2fb9e0fde059da
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 @@
         }
       }
     );
+    /**
+     * @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 @@
     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 @@
         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 {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 @@
     };
   }
 
+  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();