autoAcceptIfNoUser (#6759)

* autoAcceptIfNoUser for desktop * autoAcceptIfNoUser for terminal * autoAcceptIfNoUser for files * forgot few extra files --------- Signed-off-by: si458 <simonsmith5521@gmail.com>

Simon Smith committed Feb 9, 2025 at 19:54 UTC 711bb56a93d27a04a4c102c8a9c8a56ff415fbc9
6 files changed +290 -240
agents/meshcore.js
+278 -240
@@ -1158,6 +1158,7 @@ function handleServerCommand(data) {
1158 tunnel.soptions = data.soptions;
1159 tunnel.consentTimeout = (tunnel.soptions && tunnel.soptions.consentTimeout) ? tunnel.soptions.consentTimeout : 30;
1160 tunnel.consentAutoAccept = (tunnel.soptions && (tunnel.soptions.consentAutoAccept === true));
1161 + tunnel.consentAutoAcceptIfNoUser = (tunnel.soptions && (tunnel.soptions.consentAutoAcceptIfNoUser === true));
1162 tunnel.oldStyle = (tunnel.soptions && tunnel.soptions.oldStyle) ? tunnel.soptions.oldStyle : false;
1163 tunnel.tcpaddr = data.tcpaddr;
1164 tunnel.tcpport = data.tcpport;
@@ -2297,6 +2298,58 @@ function terminal_end()
2298
2299 }
2300
2301 +function terminal_consent_ask(ws) {
2302 + ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2303 + var consentMessage = currentTranslation['terminalConsent'].replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username);
2304 + var consentTitle = 'MeshCentral';
2305 + if (ws.httprequest.soptions != null) {
2306 + if (ws.httprequest.soptions.consentTitle != null) { consentTitle = ws.httprequest.soptions.consentTitle; }
2307 + if (ws.httprequest.soptions.consentMsgTerminal != null) { consentMessage = ws.httprequest.soptions.consentMsgTerminal.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
2308 + }
2309 + if (process.platform == 'win32') {
2310 + var enhanced = false;
2311 + if (ws.httprequest.oldStyle === false) {
2312 + try { require('win-userconsent'); enhanced = true; } catch (ex) { }
2313 + }
2314 + if (enhanced) {
2315 + var ipr = server_getUserImage(ws.httprequest.userid);
2316 + ipr.consentTitle = consentTitle;
2317 + ipr.consentMessage = consentMessage;
2318 + ipr.consentTimeout = ws.httprequest.consentTimeout;
2319 + ipr.consentAutoAccept = ws.httprequest.consentAutoAccept;
2320 + ipr.username = ws.httprequest.realname;
2321 + ipr.translations = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
2322 + ws.httprequest.tpromise._consent = ipr.then(function (img) {
2323 + this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translations, background: color_options.background, foreground: color_options.foreground });
2324 + this.__childPromise.close = this.consent.close.bind(this.consent);
2325 + return (this.consent);
2326 + });
2327 + } else {
2328 + ws.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout);
2329 + }
2330 + } else {
2331 + ws.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout);
2332 + }
2333 + ws.httprequest.tpromise._consent.retPromise = ws.httprequest.tpromise;
2334 + ws.httprequest.tpromise._consent.then(function (always) {
2335 + if (always && process.platform == 'win32') { server_set_consentTimer(this.retPromise.httprequest.userid); }
2336 + // Success
2337 + MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
2338 + this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
2339 + this.retPromise._consent = null;
2340 + this.retPromise._res();
2341 + }, function (e) {
2342 + if (this.retPromise.that) {
2343 + if(this.retPromise.that.httprequest){ // User Consent Denied
2344 + MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
2345 + } else { } // Connection was closed server side, maybe log some messages somewhere?
2346 + this.retPromise._consent = null;
2347 + this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2348 + } else { } // no websocket, maybe log some messages somewhere?
2349 + this.retPromise._rej(e.toString());
2350 + });
2351 +}
2352 +
2353 function terminal_promise_connection_rejected(e)
2354 {
2355 // FAILED to connect terminal
@@ -2609,6 +2662,101 @@ function kvm_tunnel_consentpromise_closehandler()
2662 if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); }
2663 }
2664
2665 +function kvm_consent_ok(ws) {
2666 + // User Consent Prompt is not required because no user is present
2667 + if (ws.httprequest.consent && (ws.httprequest.consent & 1)){
2668 + // User Notifications is required
2669 + MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
2670 + var notifyMessage = currentTranslation['desktopNotify'].replace('{0}', ws.httprequest.realname);
2671 + var notifyTitle = "MeshCentral";
2672 + if (ws.httprequest.soptions != null) {
2673 + if (ws.httprequest.soptions.notifyTitle != null) { notifyTitle = ws.httprequest.soptions.notifyTitle; }
2674 + if (ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = ws.httprequest.soptions.notifyMsgDesktop.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
2675 + }
2676 + try { require('toaster').Toast(notifyTitle, notifyMessage, ws.tsid); } catch (ex) { }
2677 + } else {
2678 + MeshServerLogEx(36, null, "Started remote desktop without notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
2679 + }
2680 + if (ws.httprequest.consent && (ws.httprequest.consent & 0x40)) {
2681 + // Connection Bar is required
2682 + if (ws.httprequest.desktop.kvm.connectionBar) {
2683 + ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
2684 + ws.httprequest.desktop.kvm.connectionBar.close();
2685 + }
2686 + try {
2687 + ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(ws.httprequest.privacybartext.replace('{0}', ws.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', ws.httprequest.desktop.kvm.users.join(', ')).replace(/'/g, "\\'\\"), require('MeshAgent')._tsid, color_options);
2688 + MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
2689 + } catch (ex) {
2690 + MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or not Supported (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
2691 + }
2692 + if (ws.httprequest.desktop.kvm.connectionBar) {
2693 + ws.httprequest.desktop.kvm.connectionBar.state = {
2694 + userid: ws.httprequest.userid,
2695 + xuserid: ws.httprequest.xuserid,
2696 + username: ws.httprequest.username,
2697 + sessionid: ws.httprequest.sessionid,
2698 + remoteaddr: ws.httprequest.remoteaddr,
2699 + guestname: ws.httprequest.guestname,
2700 + desktop: ws.httprequest.desktop
2701 + };
2702 + ws.httprequest.desktop.kvm.connectionBar.on('close', function () {
2703 + console.info1('Connection Bar Forcefully closed');
2704 + MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.state.remoteaddr + ")", this.state);
2705 + for (var i in this.state.desktop.kvm._pipedStreams) {
2706 + this.state.desktop.kvm._pipedStreams[i].end();
2707 + }
2708 + this.state.desktop.kvm.end();
2709 + });
2710 + }
2711 + }
2712 + ws.httprequest.desktop.kvm.pipe(ws, { dataTypeSkip: 1 });
2713 + if (ws.httprequest.autolock) {
2714 + destopLockHelper_pipe(ws.httprequest);
2715 + }
2716 +}
2717 +
2718 +function kvm_consent_ask(ws){
2719 + // Send a console message back using the console channel, "\n" is supported.
2720 + ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2721 + var consentMessage = currentTranslation['desktopConsent'].replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username);
2722 + var consentTitle = 'MeshCentral';
2723 + if (ws.httprequest.soptions != null) {
2724 + if (ws.httprequest.soptions.consentTitle != null) { consentTitle = ws.httprequest.soptions.consentTitle; }
2725 + if (ws.httprequest.soptions.consentMsgDesktop != null) { consentMessage = ws.httprequest.soptions.consentMsgDesktop.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
2726 + }
2727 + var pr;
2728 + if (process.platform == 'win32') {
2729 + var enhanced = false;
2730 + if (ws.httprequest.oldStyle === false) {
2731 + try { require('win-userconsent'); enhanced = true; } catch (ex) { }
2732 + }
2733 + if (enhanced) {
2734 + var ipr = server_getUserImage(ws.httprequest.userid);
2735 + ipr.consentTitle = consentTitle;
2736 + ipr.consentMessage = consentMessage;
2737 + ipr.consentTimeout = ws.httprequest.consentTimeout;
2738 + ipr.consentAutoAccept = ws.httprequest.consentAutoAccept;
2739 + ipr.tsid = this.tsid;
2740 + ipr.username = ws.httprequest.realname;
2741 + ipr.translation = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
2742 + pr = ipr.then(function (img) {
2743 + this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), uid: this.tsid, timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translation, background: color_options.background, foreground: color_options.foreground });
2744 + this.__childPromise.close = this.consent.close.bind(this.consent);
2745 + return (this.consent);
2746 + });
2747 + } else {
2748 + pr = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout, null, this.tsid);
2749 + }
2750 + } else {
2751 + pr = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout, null, this.tsid);
2752 + }
2753 + pr.ws = ws;
2754 + ws.pause();
2755 + ws._consentpromise = pr;
2756 + ws.prependOnceListener('end', kvm_tunnel_consentpromise_closehandler);
2757 + pr.then(kvm_consentpromise_resolved, kvm_consentpromise_rejected);
2758 +}
2759 +
2760 function kvm_consentpromise_rejected(e)
2761 {
2762 if (this.ws) {
@@ -2688,6 +2836,66 @@ function kvm_consentpromise_resolved(always)
2836 this.ws = null;
2837 }
2838
2839 +function files_consent_ok(ws){
2840 + // User Consent Prompt is not required
2841 + if (ws.httprequest.consent && (ws.httprequest.consent & 4)) {
2842 + // User Notifications is required
2843 + MeshServerLogEx(42, null, "Started remote files with toast notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
2844 + var notifyMessage = currentTranslation['fileNotify'].replace('{0}', ws.httprequest.realname);
2845 + var notifyTitle = "MeshCentral";
2846 + if (ws.httprequest.soptions != null) {
2847 + if (ws.httprequest.soptions.notifyTitle != null) { notifyTitle = ws.httprequest.soptions.notifyTitle; }
2848 + if (ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = ws.httprequest.soptions.notifyMsgFiles.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
2849 + }
2850 + try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
2851 + } else {
2852 + MeshServerLogEx(43, null, "Started remote files without notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
2853 + }
2854 + ws.resume();
2855 +}
2856 +
2857 +function files_consent_ask(ws){
2858 + // Send a console message back using the console channel, "\n" is supported.
2859 + ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2860 + var consentMessage = currentTranslation['fileConsent'].replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username);
2861 + var consentTitle = 'MeshCentral';
2862 +
2863 + if (ws.httprequest.soptions != null) {
2864 + if (ws.httprequest.soptions.consentTitle != null) { consentTitle = ws.httprequest.soptions.consentTitle; }
2865 + if (ws.httprequest.soptions.consentMsgFiles != null) { consentMessage = ws.httprequest.soptions.consentMsgFiles.replace('{0}', ws.httprequest.realname).replace('{1}', ws.httprequest.username); }
2866 + }
2867 + var pr;
2868 + if (process.platform == 'win32') {
2869 + var enhanced = false;
2870 + if (ws.httprequest.oldStyle === false) {
2871 + try { require('win-userconsent'); enhanced = true; } catch (ex) { }
2872 + }
2873 + if (enhanced) {
2874 + var ipr = server_getUserImage(ws.httprequest.userid);
2875 + ipr.consentTitle = consentTitle;
2876 + ipr.consentMessage = consentMessage;
2877 + ipr.consentTimeout = ws.httprequest.consentTimeout;
2878 + ipr.consentAutoAccept = ws.httprequest.consentAutoAccept;
2879 + ipr.username = ws.httprequest.realname;
2880 + ipr.translations = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
2881 + pr = ipr.then(function (img) {
2882 + this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translations, background: color_options.background, foreground: color_options.foreground });
2883 + this.__childPromise.close = this.consent.close.bind(this.consent);
2884 + return (this.consent);
2885 + });
2886 + } else {
2887 + pr = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout, null);
2888 + }
2889 + } else {
2890 + pr = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout, null);
2891 + }
2892 + pr.ws = ws;
2893 + ws.pause();
2894 + ws._consentpromise = pr;
2895 + ws.prependOnceListener('end', files_tunnel_endhandler);
2896 + pr.then(files_consentpromise_resolved, files_consentpromise_rejected);
2897 +}
2898 +
2899 function files_consentpromise_resolved(always)
2900 {
2901 if (always && process.platform == 'win32') { server_set_consentTimer(this.ws.httprequest.userid); }
@@ -2817,76 +3025,31 @@ function onTunnelData(data)
3025 this.end = terminal_end;
3026
3027 // Perform User-Consent if needed.
2820 - if (this.httprequest.consent && (this.httprequest.consent & 16))
2821 - {
2822 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2823 - var consentMessage = currentTranslation['terminalConsent'].replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username);
2824 - var consentTitle = 'MeshCentral';
2825 -
2826 - if (this.httprequest.soptions != null)
2827 - {
2828 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
2829 - if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2830 - }
2831 - if (process.platform == 'win32')
2832 - {
2833 - var enhanced = false;
2834 - if (this.httprequest.oldStyle === false) {
2835 - try { require('win-userconsent'); enhanced = true; } catch (ex) { }
2836 - }
2837 - if (enhanced)
2838 - {
2839 - var ipr = server_getUserImage(this.httprequest.userid);
2840 - ipr.consentTitle = consentTitle;
2841 - ipr.consentMessage = consentMessage;
2842 - ipr.consentTimeout = this.httprequest.consentTimeout;
2843 - ipr.consentAutoAccept = this.httprequest.consentAutoAccept;
2844 - ipr.username = this.httprequest.realname;
2845 - ipr.translations = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
2846 - this.httprequest.tpromise._consent = ipr.then(function (img)
2847 - {
2848 - this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translations, background: color_options.background, foreground: color_options.foreground });
2849 - this.__childPromise.close = this.consent.close.bind(this.consent);
2850 - return (this.consent);
2851 - });
2852 - } else
2853 - {
2854 - this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, this.httprequest.consentTimeout);
2855 - }
2856 - } else
2857 - {
2858 - this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, this.httprequest.consentTimeout);
2859 - }
2860 - this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise;
2861 - this.httprequest.tpromise._consent.then(
2862 - function (always)
2863 - {
2864 - if (always && process.platform == 'win32') { server_set_consentTimer(this.retPromise.httprequest.userid); }
2865 -
2866 - // Success
2867 - MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
2868 - this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
2869 - this.retPromise._consent = null;
2870 - this.retPromise._res();
2871 - },
2872 - function (e) {
2873 - if (this.retPromise.that) {
2874 - if(this.retPromise.that.httprequest){ // User Consent Denied
2875 - MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
2876 - } else { } // Connection was closed server side, maybe log some messages somewhere?
2877 - this.retPromise._consent = null;
2878 - this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2879 - } else { } // no websocket, maybe log some messages somewhere?
2880 - this.retPromise._rej(e.toString());
3028 + if (this.httprequest.consent && (this.httprequest.consent & 16)) {
3029 + // User asked for consent so now we check if we can auto accept if no user is present/loggedin
3030 + if (this.httprequest.consentAutoAcceptIfNoUser) {
3031 + var p = require('user-sessions').enumerateUsers();
3032 + p.sessionid = this.httprequest.sessionid;
3033 + p.ws = this;
3034 + p.then(function (u) {
3035 + var v = [];
3036 + for (var i in u) {
3037 + if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
3038 + }
3039 + if (v.length == 0) { // No user is present, auto accept
3040 + this.ws.httprequest.tpromise._res();
3041 + } else {
3042 + // User is present so we still need consent
3043 + terminal_consent_ask(this.ws);
3044 + }
3045 });
2882 - }
2883 - else
2884 - {
3046 + } else {
3047 + terminal_consent_ask(this);
3048 + }
3049 + } else {
3050 // User-Consent is not required, so just resolve this promise
3051 this.httprequest.tpromise._res();
3052 }
2888 -
2889 -
3053 this.httprequest.tpromise.then(terminal_promise_consent_resolved, terminal_promise_consent_rejected);
3054 }
3055 else if (this.httprequest.protocol == 2)
@@ -2910,6 +3073,7 @@ function onTunnelData(data)
3073 var tsid = null;
3074 if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
3075 require('MeshAgent')._tsid = tsid;
3076 + this.tsid = tsid;
3077
3078 // If MacOS, Wake up device with caffeinate
3079 if(process.platform == 'darwin'){
@@ -2981,119 +3145,33 @@ function onTunnelData(data)
3145 }
3146
3147 // Perform notification if needed. Toast messages may not be supported on all platforms.
2984 - if (this.httprequest.consent && (this.httprequest.consent & 8))
2985 - {
2986 - // User Consent Prompt is required
2987 - // Send a console message back using the console channel, "\n" is supported.
2988 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2989 - var consentMessage = currentTranslation['desktopConsent'].replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username);
2990 - var consentTitle = 'MeshCentral';
2991 - if (this.httprequest.soptions != null)
2992 - {
2993 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
2994 - if (this.httprequest.soptions.consentMsgDesktop != null) { consentMessage = this.httprequest.soptions.consentMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2995 - }
2996 - var pr;
2997 - if (process.platform == 'win32')
2998 - {
2999 - var enhanced = false;
3000 - if (this.httprequest.oldStyle === false) {
3001 - try { require('win-userconsent'); enhanced = true; } catch (ex) { }
3002 - }
3003 - if (enhanced)
3004 - {
3005 - var ipr = server_getUserImage(this.httprequest.userid);
3006 - ipr.consentTitle = consentTitle;
3007 - ipr.consentMessage = consentMessage;
3008 - ipr.consentTimeout = this.httprequest.consentTimeout;
3009 - ipr.consentAutoAccept = this.httprequest.consentAutoAccept;
3010 - ipr.tsid = tsid;
3011 - ipr.username = this.httprequest.realname;
3012 - ipr.translation = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
3013 - pr = ipr.then(function (img)
3014 - {
3015 - this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), uid: this.tsid, timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translation, background: color_options.background, foreground: color_options.foreground });
3016 - this.__childPromise.close = this.consent.close.bind(this.consent);
3017 - return (this.consent);
3018 - });
3019 - }
3020 - else
3021 - {
3022 - pr = require('message-box').create(consentTitle, consentMessage, this.httprequest.consentTimeout, null, tsid);
3023 - }
3024 - }
3025 - else
3026 - {
3027 - pr = require('message-box').create(consentTitle, consentMessage, this.httprequest.consentTimeout, null, tsid);
3148 + if (this.httprequest.consent && (this.httprequest.consent & 8)) {
3149 +
3150 + // User asked for consent but now we check if can auto accept if no user is present
3151 + if (this.httprequest.consentAutoAcceptIfNoUser) {
3152 + // Get list of users to check if we any actual users logged in, and if users logged in, we still need consent
3153 + var p = require('user-sessions').enumerateUsers();
3154 + p.sessionid = this.httprequest.sessionid;
3155 + p.ws = this;
3156 + p.then(function (u) {
3157 + var v = [];
3158 + for (var i in u) {
3159 + if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
3160 + }
3161 + if (v.length == 0) { // No user is present, auto accept
3162 + kvm_consent_ok(this.ws);
3163 + } else {
3164 + // User is present so we still need consent
3165 + kvm_consent_ask(this.ws);
3166 + }
3167 + });
3168 + } else {
3169 + // User Consent Prompt is required
3170 + kvm_consent_ask(this);
3171 }
3029 - pr.ws = this;
3030 - this.pause();
3031 - this._consentpromise = pr;
3032 - this.prependOnceListener('end', kvm_tunnel_consentpromise_closehandler);
3033 - pr.then(kvm_consentpromise_resolved, kvm_consentpromise_rejected);
3034 - }
3035 - else
3036 - {
3172 + } else {
3173 // User Consent Prompt is not required
3038 - if (this.httprequest.consent && (this.httprequest.consent & 1))
3039 - {
3040 - // User Notifications is required
3041 - MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
3042 - var notifyMessage = currentTranslation['desktopNotify'].replace('{0}', this.httprequest.realname);
3043 - var notifyTitle = "MeshCentral";
3044 - if (this.httprequest.soptions != null) {
3045 - if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
3046 - if (this.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
3047 - }
3048 - try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (ex) { }
3049 - } else
3050 - {
3051 - MeshServerLogEx(36, null, "Started remote desktop without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
3052 - }
3053 - if (this.httprequest.consent && (this.httprequest.consent & 0x40))
3054 - {
3055 - // Connection Bar is required
3056 - if (this.httprequest.desktop.kvm.connectionBar)
3057 - {
3058 - this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
3059 - this.httprequest.desktop.kvm.connectionBar.close();
3060 - }
3061 - try
3062 - {
3063 - this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', this.httprequest.desktop.kvm.users.join(', ')).replace(/'/g, "\\'\\"), require('MeshAgent')._tsid, color_options);
3064 - MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + this.httprequest.remoteaddr + ")", this.httprequest);
3065 - } catch (ex) {
3066 - MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or not Supported (" + this.httprequest.remoteaddr + ")", this.httprequest);
3067 - }
3068 - if (this.httprequest.desktop.kvm.connectionBar)
3069 - {
3070 - this.httprequest.desktop.kvm.connectionBar.state =
3071 - {
3072 - userid: this.httprequest.userid,
3073 - xuserid: this.httprequest.xuserid,
3074 - username: this.httprequest.username,
3075 - sessionid: this.httprequest.sessionid,
3076 - remoteaddr: this.httprequest.remoteaddr,
3077 - guestname: this.httprequest.guestname,
3078 - desktop: this.httprequest.desktop
3079 - };
3080 - this.httprequest.desktop.kvm.connectionBar.on('close', function ()
3081 - {
3082 - console.info1('Connection Bar Forcefully closed');
3083 - MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.state.remoteaddr + ")", this.state);
3084 - for (var i in this.state.desktop.kvm._pipedStreams)
3085 - {
3086 - this.state.desktop.kvm._pipedStreams[i].end();
3087 - }
3088 - this.state.desktop.kvm.end();
3089 - });
3090 - }
3091 - }
3092 - this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 });
3093 - if (this.httprequest.autolock)
3094 - {
3095 - destopLockHelper_pipe(this.httprequest);
3096 - }
3174 + kvm_consent_ok(this);
3175 }
3176
3177 this.removeAllListeners('data');
@@ -3137,71 +3215,31 @@ function onTunnelData(data)
3215 // Perform notification if needed. Toast messages may not be supported on all platforms.
3216 if (this.httprequest.consent && (this.httprequest.consent & 32))
3217 {
3140 - // User Consent Prompt is required
3141 - // Send a console message back using the console channel, "\n" is supported.
3142 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
3143 - var consentMessage = currentTranslation['fileConsent'].replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username);
3144 - var consentTitle = 'MeshCentral';
3145 -
3146 - if (this.httprequest.soptions != null)
3147 - {
3148 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
3149 - if (this.httprequest.soptions.consentMsgFiles != null) { consentMessage = this.httprequest.soptions.consentMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
3150 - }
3151 - var pr;
3152 - if (process.platform == 'win32')
3153 - {
3154 - var enhanced = false;
3155 - if (this.httprequest.oldStyle === false) {
3156 - try { require('win-userconsent'); enhanced = true; } catch (ex) { }
3157 - }
3158 - if (enhanced)
3159 - {
3160 - var ipr = server_getUserImage(this.httprequest.userid);
3161 - ipr.consentTitle = consentTitle;
3162 - ipr.consentMessage = consentMessage;
3163 - ipr.consentTimeout = this.httprequest.consentTimeout;
3164 - ipr.consentAutoAccept = this.httprequest.consentAutoAccept;
3165 - ipr.username = this.httprequest.realname;
3166 - ipr.translations = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
3167 - pr = ipr.then(function (img)
3168 - {
3169 - this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translations, background: color_options.background, foreground: color_options.foreground });
3170 - this.__childPromise.close = this.consent.close.bind(this.consent);
3171 - return (this.consent);
3172 - });
3173 - } else
3174 - {
3175 - pr = require('message-box').create(consentTitle, consentMessage, this.httprequest.consentTimeout, null);
3176 - }
3177 - }
3178 - else
3179 - {
3180 - pr = require('message-box').create(consentTitle, consentMessage, this.httprequest.consentTimeout, null);
3181 - }
3182 - pr.ws = this;
3183 - this.pause();
3184 - this._consentpromise = pr;
3185 - this.prependOnceListener('end', files_tunnel_endhandler);
3186 - pr.then(files_consentpromise_resolved, files_consentpromise_rejected);
3187 - }
3188 - else
3189 - {
3190 - // User Consent Prompt is not required
3191 - if (this.httprequest.consent && (this.httprequest.consent & 4)) {
3192 - // User Notifications is required
3193 - MeshServerLogEx(42, null, "Started remote files with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
3194 - var notifyMessage = currentTranslation['fileNotify'].replace('{0}', this.httprequest.realname);
3195 - var notifyTitle = "MeshCentral";
3196 - if (this.httprequest.soptions != null) {
3197 - if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
3198 - if (this.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.httprequest.soptions.notifyMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
3199 - }
3200 - try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
3218 + // User asked for consent so now we check if we can auto accept if no user is present/loggedin
3219 + if (this.httprequest.consentAutoAcceptIfNoUser) {
3220 + var p = require('user-sessions').enumerateUsers();
3221 + p.sessionid = this.httprequest.sessionid;
3222 + p.ws = this;
3223 + p.then(function (u) {
3224 + var v = [];
3225 + for (var i in u) {
3226 + if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
3227 + }
3228 + if (v.length == 0) { // No user is present, auto accept
3229 + // User Consent Prompt is not required
3230 + files_consent_ok(this.ws);
3231 + } else {
3232 + // User is present so we still need consent
3233 + files_consent_ask(this.ws);
3234 + }
3235 + });
3236 } else {
3202 - MeshServerLogEx(43, null, "Started remote files without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
3237 + // User Consent Prompt is required
3238 + files_consent_ask(this);
3239 }
3204 - this.resume();
3240 + } else {
3241 + // User Consent Prompt is not required
3242 + files_consent_ok(this);
3243 }
3244
3245 // Setup files
meshcentral-config-schema.json
+5
@@ -1972,6 +1972,11 @@
1972 "default": false,
1973 "description": "If true, user consent is accepted after the timeout."
1974 },
1975 + "autoAcceptIfNoUser": {
1976 + "type": "boolean",
1977 + "default": false,
1978 + "description": "If true, user consent is accepted if no user is logged in."
1979 + },
1980 "oldStyle": {
1981 "type": "boolean",
1982 "default": false,
meshdesktopmultiplex.js
+1
@@ -1347,6 +1347,7 @@ function CreateMeshRelayEx2(parent, ws, req, domain, user, cookie) {
1347 if (typeof domain.consentmessages.files == 'string') { command.soptions.consentMsgFiles = domain.consentmessages.files; }
1348 if ((typeof domain.consentmessages.consenttimeout == 'number') && (domain.consentmessages.consenttimeout > 0)) { command.soptions.consentTimeout = domain.consentmessages.consenttimeout; }
1349 if (domain.consentmessages.autoacceptontimeout === true) { command.soptions.consentAutoAccept = true; }
1350 + if (domain.consentmessages.autoacceptifnouser === true) { command.soptions.consentAutoAcceptIfNoUser = true; }
1351 if (domain.consentmessages.oldstyle === true) { command.soptions.oldStyle = true; }
1352 }
1353 if (typeof domain.notificationmessages == 'object') {
meshrelay.js
+4
@@ -896,6 +896,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
896 if (typeof domain.consentmessages.files == 'string') { command.soptions.consentMsgFiles = domain.consentmessages.files; }
897 if ((typeof domain.consentmessages.consenttimeout == 'number') && (domain.consentmessages.consenttimeout > 0)) { command.soptions.consentTimeout = domain.consentmessages.consenttimeout; }
898 if (domain.consentmessages.autoacceptontimeout === true) { command.soptions.consentAutoAccept = true; }
899 + if (domain.consentmessages.autoacceptifnouser === true) { command.soptions.consentAutoAcceptIfNoUser = true; }
900 if (domain.consentmessages.oldstyle === true) { command.soptions.oldStyle = true; }
901 }
902 if (typeof domain.notificationmessages == 'object') {
@@ -934,6 +935,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
935 if (typeof domain.consentmessages.files == 'string') { command.soptions.consentMsgFiles = domain.consentmessages.files; }
936 if ((typeof domain.consentmessages.consenttimeout == 'number') && (domain.consentmessages.consenttimeout > 0)) { command.soptions.consentTimeout = domain.consentmessages.consenttimeout; }
937 if (domain.consentmessages.autoacceptontimeout === true) { command.soptions.consentAutoAccept = true; }
938 + if (domain.consentmessages.autoacceptifnouser === true) { command.soptions.consentAutoAcceptIfNoUser = true; }
939 if (domain.consentmessages.oldstyle === true) { command.soptions.oldStyle = true; }
940 }
941 if (typeof domain.notificationmessages == 'object') {
@@ -952,6 +954,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
954 if (typeof domain.consentmessages.files == 'string') { command.soptions.consentMsgFiles = domain.consentmessages.files; }
955 if ((typeof domain.consentmessages.consenttimeout == 'number') && (domain.consentmessages.consenttimeout > 0)) { command.soptions.consentTimeout = domain.consentmessages.consenttimeout; }
956 if (domain.consentmessages.autoacceptontimeout === true) { command.soptions.consentAutoAccept = true; }
957 + if (domain.consentmessages.autoacceptifnouser === true) { command.soptions.consentAutoAcceptIfNoUser = true; }
958 if (domain.consentmessages.oldstyle === true) { command.soptions.oldStyle = true; }
959 }
960 if (typeof domain.notificationmessages == 'object') {
@@ -1004,6 +1007,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
1007 if (typeof domain.consentmessages.files == 'string') { command.soptions.consentMsgFiles = domain.consentmessages.files; }
1008 if ((typeof domain.consentmessages.consenttimeout == 'number') && (domain.consentmessages.consenttimeout > 0)) { command.soptions.consentTimeout = domain.consentmessages.consenttimeout; }
1009 if (domain.consentmessages.autoacceptontimeout === true) { command.soptions.consentAutoAccept = true; }
1010 + if (domain.consentmessages.autoacceptifnouser === true) { command.soptions.consentAutoAcceptIfNoUser = true; }
1011 if (domain.consentmessages.oldstyle === true) { command.soptions.oldStyle = true; }
1012 }
1013 if (typeof domain.notificationmessages == 'object') {
meshuser.js
+1
@@ -997,6 +997,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
997 if (typeof domain.consentmessages.files == 'string') { command.soptions.consentMsgFiles = domain.consentmessages.files; }
998 if ((typeof domain.consentmessages.consenttimeout == 'number') && (domain.consentmessages.consenttimeout > 0)) { command.soptions.consentTimeout = domain.consentmessages.consenttimeout; }
999 if (domain.consentmessages.autoacceptontimeout === true) { command.soptions.consentAutoAccept = true; }
1000 + if (domain.consentmessages.autoacceptifnouser === true) { command.soptions.consentAutoAcceptIfNoUser = true; }
1001 if (domain.consentmessages.oldstyle === true) { command.soptions.oldStyle = true; }
1002 }
1003 if (typeof domain.notificationmessages == 'object') {
sample-config-advanced.json
+1
@@ -343,6 +343,7 @@
343 "files": "{0} requesting remote files access. Grant access?",
344 "consentTimeout": 30,
345 "autoAcceptOnTimeout": false,
346 + "autoAcceptIfNoUser": false,
347 "oldStyle": true
348 },
349 "_notificationMessages": {