Browse Source

Fix #4 Api key and Api call

isundil 2 years ago
parent
commit
974a82a616
6 changed files with 111 additions and 8 deletions
  1. 39 0
      models/apiKey.js
  2. 39 6
      router/input.js
  3. 1 0
      src/config.js
  4. 3 2
      src/databaseHelper.js
  5. 4 0
      static/public/css/style.css
  6. 25 0
      templates/api.js

+ 39 - 0
models/apiKey.js

@@ -0,0 +1,39 @@
+
+const DatabaseModel = require("./DatabaseModel.js").DatabaseModel;
+
+function ApiKeyModel(privId, ipAddress) {
+    this.created = new Date();
+    this.ipAddress = ipAddress;
+    this.apiKey = privId;
+}
+
+Object.setPrototypeOf(ApiKeyModel.prototype, DatabaseModel.prototype);
+
+ApiKeyModel.prototype.getTableName = function() {
+    return "apiKey";
+}
+
+ApiKeyModel.prototype.createOrUpdateBase = async function(dbHelper) {
+    await dbHelper.runSql(`CREATE TABLE IF NOT EXISTS 'apiKey' (
+        created datetime not null,
+        apiKey string,
+        ipAddress string
+        )`);
+}
+
+ApiKeyModel.prototype.describe = function() {
+    return {
+        "ipAddress": this.ipAddress,
+        "created": this.created.getTime(),
+        "apiKey": this.apiKey
+    };
+}
+
+ApiKeyModel.prototype.fromDb = function(dbObj) {
+    this.created = new Date(dbObj['created']);
+    this.ipAddress = dbObj['ipAddress'];
+    this.apiKey = dbObj['apiKey'];
+}
+
+module.exports.ApiKeyModel = ApiKeyModel;
+

+ 39 - 6
router/input.js

@@ -2,9 +2,11 @@
 const whiskers = require('whiskers');
 const fs = require('fs');
 
+const ApiKeyModel = require('../models/apiKey.js').ApiKeyModel;
 const PasteContent = require('../models/pasteContent.js').PasteContent;
 const mCrypto = require('../src/crypto.js');
 const Security = require('../src/security.js');
+const CONFIG = require('../src/config.js');
 
 async function renderRawPage(app, req, res, entity) {
     if (entity.type === 'paste')
@@ -23,6 +25,7 @@ async function renderPublicPage(app, req, res, entity) {
         return await app.routerUtils.staticServe(res, app.getData(entity.privId));
     else if (entity.type === 'file') {
         let data = JSON.parse(entity.data);
+        // FIXME viewer
         return await app.routerUtils.staticDownload(res, app.getData(entity.privId), data.name, data.type);
     }
     else if (entity.type === 'short')
@@ -65,13 +68,21 @@ module.exports = { register: app => {
         res.end(whiskers.render(require('../templates/pastit.js'), context));
     });
     app.router.post("/pastit", async (req, res) => {
-        const content = req.body.content;
+        const content = "" + (req.body.content || req.post);
         const privId = mCrypto.string(content);
-        const captchaOk = await Security.captchaCheck(req.body['g-recaptcha-response'], req.headers['x-forwarded-for'] || req.socket.remoteAddress);
+
+        if (req.body['g-recaptcha-response']) {
+            const captchaOk = await Security.captchaCheck(req.body['g-recaptcha-response'], req.headers['x-forwarded-for'] || req.socket.remoteAddress);
+            if (!captchaOk)
+                return app.routerUtils.jsonResponse(res, { err: "Invalid captcha input", id: null });
+        } else if (req.body['apiKey']) {
+            if (!(await app.databaseHelper.findOne(ApiKeyModel, { apiKey: req.body['apiKey'] })))
+                return app.routerUtils.jsonResponse(res, { err: "Unauthorized access", id: null });
+        } else {
+            return app.routerUtils.jsonResponse(res, { err: "Unauthorized access", id: null });
+        }
         let entity = await app.databaseHelper.findOne(PasteContent, { privId: privId });
 
-        if (!captchaOk)
-            return app.routerUtils.jsonResponse(res, { err: "Invalid captcha input", id: null });
         if (!content || !content.length)
             return app.routerUtils.jsonResponse(res, { err: "Empty input", id: null });
         if (entity && !entity.expired) {
@@ -80,11 +91,15 @@ module.exports = { register: app => {
         } else {
             entity = entity || new PasteContent(privId, "paste");
             entity.expired = false;
+            entity.apiKey = req.body['apiKey'] || null;
             entity.renew();
             fs.writeFileSync(app.getData(privId), content);
             await app.databaseHelper.upsertOne(entity);
         }
-        app.routerUtils.jsonResponse(res, { err: null, id: entity.publicId });
+        if (req.body.apiKey)
+            res.end(CONFIG.url+"/x/" +entity.publicId+"\r\n");
+        else
+            app.routerUtils.jsonResponse(res, { err: null, id: entity.publicId });
     });
 
     // URL shortener tool
@@ -94,7 +109,7 @@ module.exports = { register: app => {
         res.end(whiskers.render(require('../templates/short.js'), context));
     });
     app.router.post("/short", async (req, res) => {
-        const link = req.body.content;
+        const link = "" + req.body.content;
         const privId = mCrypto.string(await app.databaseHelper.count(PasteContent) + link);
         const captchaOk = await Security.captchaCheck(req.body['g-recaptcha-response'], req.headers['x-forwarded-for'] || req.socket.remoteAddress);
 
@@ -129,5 +144,23 @@ module.exports = { register: app => {
         await app.databaseHelper.insertOne(entity);
         app.routerUtils.jsonResponse(res, { err: null, id: entity.privId });
     });
+
+    // API page
+    app.router.get("/api", (req, res) => {
+        let context = app.routerUtils.commonRenderInfos();
+        context.page_title += " - API Usage";
+        res.end(whiskers.render(require('../templates/api.js'), context));
+    });
+    app.router.post("/api", async (req, res) => {
+        const ipAddress = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
+        const captchaOk = await Security.captchaCheck(req.body['g-recaptcha-response'], ipAddress);
+
+        if (!captchaOk)
+            return app.routerUtils.jsonResponse(res, { err: "Invalid captcha input", id: null });
+        const privKey = mCrypto.string(Date.now() + "" + await app.databaseHelper.count(ApiKeyModel) + "SALT_INPUT_API_KEY" +ipAddress);
+        const model = new ApiKeyModel(privKey, ipAddress);
+        await app.databaseHelper.insertOne(model);
+        return app.routerUtils.jsonResponse(res, { err: null, id: privKey });
+    });
 }};
 

+ 1 - 0
src/config.js

@@ -36,6 +36,7 @@ let configEntries = {};
         port: { value: 80, valid: validNumber },
         database: { value: "", valid: validNotEmptyString },
         sitename: { value: "Archives", valid: validNotEmptyString },
+        url: { value: "http://localhost/", valid: validNotEmptyString },
         reCaptchaPublic: { value: "", valid: validNotEmptyString },
         reCaptchaSecret: { value: "", valid: validNotEmptyString }
     };

+ 3 - 2
src/databaseHelper.js

@@ -2,7 +2,8 @@
 const sqlite3 = require('sqlite3');
 const CONFIG = require('./config.js');
 
-const PasteContent = require('../models/pasteContent.js').PasteContent;
+const PasteContentModel = require('../models/pasteContent.js').PasteContent;
+const ApiKeyModel = require('../models/apiKey.js').ApiKeyModel;
 
 function DatabaseHelper() {
     this.db = null;
@@ -15,7 +16,7 @@ DatabaseHelper.prototype.init = function() {
                 ko(err);
                 return;
             }
-            let types = [ PasteContent ];
+            let types = [ PasteContentModel, ApiKeyModel ];
             for (let i =0; i < types.length; ++i) {
                 let instance = new types[i]();
                 await instance.createOrUpdateBase(this);

+ 4 - 0
static/public/css/style.css

@@ -68,3 +68,7 @@ body > .page {
     display: none;
 }
 
+input#apiResult {
+    width: 30em;
+}
+

+ 25 - 0
templates/api.js

@@ -0,0 +1,25 @@
+
+module.exports = require('./header.js') +`
+
+<p>alias pastit="curl -X POST 'http://localhost:3003/pastit?apiKey=__KEY__' --data-binary '@-'"</p>
+<p>ls -l | pastit</p>
+
+<input type="text" placeholder="Your API key will show here" id="apiResult" disabled />
+<form id="form" action="#" method="POST">
+<div id="error" class="hidden"></div>
+<button class="g-recaptcha" data-sitekey="{reCaptcha_public}" data-callback="onSubmit" data-action="submit">Submit</button>
+</form>
+<script>
+function onSubmit(token) {
+    $.post(document.location.href, $("#form").serialize(), data => {
+        if (data.err) {
+            $("#error").text("Error: " +data.err).removeClass("hidden");
+        } else if (data.id) {
+            $("#error").text("").addClass("hidden");
+            $("#apiResult").val(data.id);
+            console.log(data.id);
+        }
+    });
+}
+</script>
+` + require('./footer.js');