isundil 2 年 前
コミット
68027324c3
4 ファイル変更83 行追加136 行削除
  1. 24 6
      router/api.js
  2. 5 2
      src/library.js
  3. 10 0
      src/routerUtils.js
  4. 44 128
      src/security.js

+ 24 - 6
router/api.js

@@ -1,13 +1,31 @@
 
-const fs = require('fs');
-const CONFIG = require('../src/config.js');
+const Security = require('../src/security.js');
 
 module.exports = { register: app => {
+    app.router.get("/api/access/list", (req, res) => {
+        app.routerUtils.onApiRequest(req, res);
+        app.routerUtils.jsonResponse(res, req.accessList);
+    });
+    app.router.post("/api/access/link", async (req, res) => { // /api/access/link, post: { linkId: string }
+        app.routerUtils.onApiRequest(req, res);
+        if (!req.post?.linkId?.length)
+            return app.routerUtils.httpResponse(res, 400, "Missing argument");
+        let access = Security.addLinkToSession(req, req.post.linkId);
+        app.routerUtils.jsonResponse(res, access);
+    });
+    app.router.del("/api/access/:id", (req, res) => {
+        app.routerUtils.onApiRequest(req, res);
+        Security.removeFromSession(req, req.params.id);
+        let access = Security.getAccessList(req.cookies);
+        app.routerUtils.jsonResponse(res, access);
+    });
     app.router.get("/api/media/list", async (req, res) => {
-        app.routerUtils.onRequest(req);
-        if (app.routerUtils.apiRequireLogin(req, res))
-            return;
-        app.routerUtils.jsonResponse(res, {});
+        app.routerUtils.onApiRequest(req, res);
+        app.routerUtils.jsonResponse(res, {
+            start: 0,
+            end: 0,
+            data: []
+        });
     });
 }};
 

+ 5 - 2
src/library.js

@@ -2,6 +2,7 @@ const fs = require('fs');
 const path = require('path');
 const mime = require("mime-types");
 const md5Stats = require('./md5sum.js').stats;
+const md5String = require('./md5sum.js').string;
 const FileTypeManager = require('./fileTypeManager.js');
 
 const MediaFileModel = require("../model/mediaItem.js").MediaFileModel;
@@ -31,13 +32,14 @@ File.prototype.enrich = async function() {
     }
 }
 
-File.prototype.saveDb = async function(db) {
+File.prototype.saveDb = async function(db, libraryHash) {
     let entity = new MediaFileModel();
     let _this = this;
     entity.path = this.path;
     entity.md5sum = this.checksum;
     await db.insertOne(entity);
     this.meta.photochamberImport = new Date();
+    this.meta.libraryPath = libraryHash;
     let metaEntities = Object.keys(this.meta).map(i => new MediaFileMetaModel(_this.checksum, i, _this.meta[i]));
     await db.insertMultipleSameTable(metaEntities);
 }
@@ -54,7 +56,7 @@ async function Library_doUpdate(dbHelper, lib) {
     lib.buff = lib.buff.filter(i => dbItems.indexOf(i.path) === -1);
     await enrichAll(lib);
     lib.buff = lib.buff.filter(i => !!i.checksum);
-    await Promise.allSettled(lib.buff.map(i => i.saveDb(dbHelper)));
+    await Promise.allSettled(lib.buff.map(i => i.saveDb(dbHelper, lib.dbHash)));
     lib.foundMedias = lib.foundMedias.concat(lib.buff);
     lib.buff = [];
 }
@@ -78,6 +80,7 @@ function Library_isValid(_this) {
 
 function Library(path) {
     this.path = path;
+    this.dbHash = md5String(path);
 }
 
 Library.prototype.updateLibrary = async function(dbHelper) {

+ 10 - 0
src/routerUtils.js

@@ -26,6 +26,16 @@ RouterUtils.prototype.requireLogin =function(req, res) {
     return true;
 };
 
+RouterUtils.prototype.onApiRequest = function(req, res) {
+    this.onRequest(req);
+    req.accessList = Security.getAccessList(req.cookies);
+    if (req.accessList === null) {
+        const log = Security.createSession(req);
+        res.setHeader("Set-Cookie", Security.SESSION_COOKIE +'='+log.key);
+        req.accessList = log.accessList;
+    }
+};
+
 RouterUtils.prototype.apiRequireLogin =function(req, res, validTokens) {
     if (Security.isLoggedUser(req.cookies))
     {

+ 44 - 128
src/security.js

@@ -30,7 +30,7 @@ function getSessionId(cookieObject) {
     return cookieObject?.[SESSION_COOKIE];
 }
 
-function getLoggedUser(cookieObject) {
+function getSessionObj(cookieObject) {
     let cookie = getSessionId(cookieObject);
     if (!cookie)
         return null;
@@ -39,59 +39,14 @@ function getLoggedUser(cookieObject) {
     if (!sessionEntry || sessionEntry.expire < now)
         return null;
     sessionEntry.expire = now + SESSION_TIME;
-    return sessionEntry.username;
+    return sessionEntry;
 }
 
-function isUserValid(username) {
-    return new Promise((ok, ko) => {
-        ldapReady.then(() => {
-            ldap.search(CONFIG.ldapBase, { filter: CONFIG.ldapFilter?.replaceAll('{0}', username), attributes: [ 'mail', 'cn', 'sn', 'dn' ], scope: 'one' }, (err, res) => {
-                if (err) {
-                    console.error(err);
-                    ko(err);
-                    return;
-                }
-                let found = null;
-                res.on('searchEntry', i => found = i.object);
-                res.on('error', console.error);
-                res.on('end', () => {
-                    if (!found) {
-                        ko();
-                        return;
-                    }
-                    const now = (new Date()).getTime();
-                    let sessionEntry = {
-                        loginDateTime: now,
-                        expire: now + SESSION_TIME,
-                        username: found.cn || found.sn,
-                        mail: Array.isArray(found.mail) ? found.mail[0] : found.mail,
-                        dn: found.dn,
-                        random: Math.random()
-                    };
-                    ok(sessionEntry);
-                });
-            });
-        });
-    });
-}
-
-function checkPassword(dn, password) {
-    return new Promise((ok, ko) => {
-        let ldap = require('ldapjs').createClient({ url: [ CONFIG.ldapUrl, CONFIG.ldapUrl ]});
-        ldap.on('error', err => {
-            console.error("Login failure: " +err);
-            ko(err.message);
-        });
-        ldap.bind(dn, password, err => {
-            if (err) {
-                console.error("Login failure: " +err);
-                ko(err instanceof ldapjs.InvalidCredentialsError ? undefined : err.message);
-                return;
-            }
-            ldap.unbind();
-            ok();
-        });
-    });
+function getAccessList(cookieObject) {
+    let session = getSessionObj(cookieObject);
+    if (!session)
+        return null;
+    return session.accessList;
 }
 
 function getRequestIp(req) {
@@ -102,90 +57,51 @@ function sign(msg) {
     return crypto.sign('sha256', Buffer.from(msg), decodeKey(CONFIG.privKey)).toString('base64');
 }
 
-function checkSign(remoteHostConfig, message, signature) {
-    let result = false;
-    try {
-        result = crypto.verify('sha256', Buffer.from(message), decodeKey(remoteHostConfig.pubKey), Buffer.from(signature, 'base64'));
-    } catch (e) {
-        console.error(e);
-        result = false;
-    }
-    if (!result)
-        console.error("Crypto::checkSign failed to check signature for message !");
-    return result;
+function Access() {
 }
+Access.prototype.id = function() { return ""; }
 
-function encodeKey(key) { return btoa(key); }
-function decodeKey(key) { return atob(key); }
-
-function generateKeys() {
-    return new Promise((ok, ko) => {
-        crypto.generateKeyPair('rsa', {
-            modulusLength: 4096,
-            publicKeyEncoding: { type: 'spki', format: 'pem' },
-            privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
-        }, (err, pubKey, privKey) => {
-            if (err) {
-                ko(err);
-                return;
-            }
-            ok({pubKey: encodeKey(pubKey), privKey: encodeKey(privKey)});
-        });
-    });
+function LinkAccess(linkId) {
+    Access.call(this);
+    this.linkId = linkId;
 }
+LinkAccess.prototype = Object.create(Access.prototype);
+LinkAccess.prototype.id = function() { return "LINK_"+this.linkId; }
 
 module.exports = {
-    isLoggedUser: (cookieObject) => {
-        return getLoggedUser(cookieObject) !== null;
-    },
-    getLoggedUser: getLoggedUser,
+    getAccessList: getAccessList,
     getRequestIp: getRequestIp,
-    getSessionId: (cookieObj) => { return getLoggedUser(cookieObj) ? getSessionId(cookieObj) : null },
-    forceLogin: (app, req) => {
-        if (!CONFIG.DEBUG_forceLogin)
-            throw "Security::ForceLogin: Access Denied";
-        const wrongCredentialsMsg = "Invalid username or password";
-        return new Promise((ok, ko) => {
-            isUserValid(CONFIG.DEBUG_forceLogin).catch(err => {
-                ko(err ? ("Internal error: " +err) : wrongCredentialsMsg);
-            }).then(sessionInfos => {
-                sessionInfos.userAgent = req.headers['user-agent'];
-                sessionInfos.instance = CONFIG.instanceHostname;
-                sessionInfos.ipAddress = getRequestIp(req);
-                let sessionKey = MD5(JSON.stringify(sessionInfos));
-                sessionInfos.sessionId = sessionKey;
-                loggedCache[sessionKey] = sessionInfos;
-                let sessionDbModel = new SessionModel(sessionInfos);
-                app.databaseHelper.insertOne(new SessionModel(sessionInfos));
-                ok(sessionKey);
-            });
-        });
+    createSession: req => {
+        const now = Date.now();
+        let sessionInfos = {
+            loginDateTime: now,
+            expire: now + SESSION_TIME,
+            accessList: {},
+            random: Math.random(),
+            userAgent: req.headers['user-agent'],
+            ipAddress: getRequestIp(req)
+        };
+        let sessionKey = MD5(JSON.stringify(sessionInfos));
+        sessionInfos.sessionId = sessionKey;
+        loggedCache[sessionKey] = sessionInfos;
+        req.cookies[SESSION_COOKIE] = sessionKey;
+        return { key: sessionKey, accessList: sessionInfos.accessList };
+    },
+    addLinkToSession: (req, linkId) => {
+        let session = getSessionObj(req.cookies);
+        if (!session)
+            return;
+        let accessList = new LinkAccess(linkId);
+        session.accessList[accessList.id()] = accessList;
+        return session.accessList;
     },
-    tryLogin: (app, req, username, password) => {
-        const wrongCredentialsMsg = "Invalid username or password";
-        return new Promise((ok, ko) => {
-            isUserValid(username).catch(err => {
-                ko(err ? ("Internal error: " +err) : wrongCredentialsMsg);
-            }).then(sessionInfos => {
-                return checkPassword(sessionInfos.dn, password).then(() => {
-                    sessionInfos.userAgent = req.headers['user-agent'];
-                    sessionInfos.instance = CONFIG.instanceHostname;
-                    sessionInfos.ipAddress = getRequestIp(req);
-                    let sessionKey = MD5(JSON.stringify(sessionInfos));
-                    sessionInfos.sessionId = sessionKey;
-                    loggedCache[sessionKey] = sessionInfos;
-                    let sessionDbModel = new SessionModel(sessionInfos);
-                    app.databaseHelper.insertOne(new SessionModel(sessionInfos));
-                    ok(sessionKey);
-                });
-            }).catch(err => {
-                ko(err ? ("Internal error: " +err) : wrongCredentialsMsg);
-            });
-        });
+    removeFromSession: (req, accessId) => {
+        let session = getSessionObj(req.cookies);
+        if (!session)
+            return;
+        delete session.accessList[accessId];
+        return session.accessList;
     },
-    sign: sign,
-    checkSign: checkSign,
-    generateKeys: generateKeys,
     SESSION_COOKIE: SESSION_COOKIE
 };