mirror of
				https://github.com/Ylianst/MeshCentral.git
				synced 2025-03-09 15:40:18 +00:00 
			
		
		
		
	Partial work on mobile device 2FA.
This commit is contained in:
		
							parent
							
								
									444e9e43e0
								
							
						
					
					
						commit
						5cdfd7e0b9
					
				
					 4 changed files with 139 additions and 22 deletions
				
			
		
							
								
								
									
										44
									
								
								meshagent.js
									
										
									
									
									
								
							
							
						
						
									
										44
									
								
								meshagent.js
									
										
									
									
									
								
							| 
						 | 
					@ -1516,6 +1516,50 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                case '2faauth': {
 | 
				
			||||||
 | 
					                    // Validate input
 | 
				
			||||||
 | 
					                    if ((typeof command.url != 'string') || (typeof command.approved != 'boolean') || (command.url.startsWith('2fa://') == false)) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // parse the URL
 | 
				
			||||||
 | 
					                    var url = null;
 | 
				
			||||||
 | 
					                    try { url = require('url').parse(command.url); } catch (ex) { }
 | 
				
			||||||
 | 
					                    if (url == null) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // For now, do nothing if authentication is not approved.
 | 
				
			||||||
 | 
					                    if (command.approve == false) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Decode the cookie
 | 
				
			||||||
 | 
					                    var urlSplit = url.query.split('&c=');
 | 
				
			||||||
 | 
					                    if (urlSplit.length != 2) return;
 | 
				
			||||||
 | 
					                    const authCookie = parent.parent.decodeCookie(urlSplit[1], null, 1);
 | 
				
			||||||
 | 
					                    if ((authCookie == null) || (typeof authCookie.c != 'string') || (('code=' + authCookie.c) != urlSplit[0])) return;
 | 
				
			||||||
 | 
					                    if ((typeof authCookie.n != 'string') || (authCookie.n != obj.dbNodeKey) || (typeof authCookie.u != 'string')) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Fetch the user
 | 
				
			||||||
 | 
					                    const user = parent.users[authCookie.u];
 | 
				
			||||||
 | 
					                    if (user == null) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Add this device as the authentication push notification device for this user
 | 
				
			||||||
 | 
					                    if (authCookie.a == 'addAuth') {
 | 
				
			||||||
 | 
					                        // Change the user
 | 
				
			||||||
 | 
					                        user.otpdev = obj.dbNodeKey;
 | 
				
			||||||
 | 
					                        parent.db.SetUser(user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Notify change
 | 
				
			||||||
 | 
					                        var targets = ['*', 'server-users', user._id];
 | 
				
			||||||
 | 
					                        if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
 | 
				
			||||||
 | 
					                        var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msgid: 113, msg: "Added push notification authentication device", domain: domain.id };
 | 
				
			||||||
 | 
					                        if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come.
 | 
				
			||||||
 | 
					                        parent.parent.DispatchEvent(targets, obj, event);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Complete 2FA checking
 | 
				
			||||||
 | 
					                    if (authCookie.a == 'checkAuth') {
 | 
				
			||||||
 | 
					                        // TODO
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                default: {
 | 
					                default: {
 | 
				
			||||||
                    parent.agentStats.unknownAgentActionCount++;
 | 
					                    parent.agentStats.unknownAgentActionCount++;
 | 
				
			||||||
                    parent.parent.debug('agent', 'Unknown agent action (' + obj.remoteaddrport + '): ' + JSON.stringify(command) + '.');
 | 
					                    parent.parent.debug('agent', 'Unknown agent action (' + obj.remoteaddrport + '): ' + JSON.stringify(command) + '.');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										85
									
								
								meshuser.js
									
										
									
									
									
								
							
							
						
						
									
										85
									
								
								meshuser.js
									
										
									
									
									
								
							| 
						 | 
					@ -1284,7 +1284,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
 | 
				
			||||||
                            break;
 | 
					                            break;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        case 'showpaths': {
 | 
					                        case 'showpaths': {
 | 
				
			||||||
                            r =  'Parent:     ' + parent.parent.parentpath + '\r\n';
 | 
					                            r = 'Parent:     ' + parent.parent.parentpath + '\r\n';
 | 
				
			||||||
                            r += 'Data:       ' + parent.parent.datapath + '\r\n';
 | 
					                            r += 'Data:       ' + parent.parent.datapath + '\r\n';
 | 
				
			||||||
                            r += 'Files:      ' + parent.parent.filespath + '\r\n';
 | 
					                            r += 'Files:      ' + parent.parent.filespath + '\r\n';
 | 
				
			||||||
                            r += 'Backup:     ' + parent.parent.backuppath + '\r\n';
 | 
					                            r += 'Backup:     ' + parent.parent.backuppath + '\r\n';
 | 
				
			||||||
| 
						 | 
					@ -1338,7 +1338,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        case 'relays': {
 | 
					                        case 'relays': {
 | 
				
			||||||
                            for (var i in parent.wsrelays) {
 | 
					                            for (var i in parent.wsrelays) {
 | 
				
			||||||
                                r += 'id: ' + i + ', ' + ((parent.wsrelays[i].state == 2)?'connected':'pending');
 | 
					                                r += 'id: ' + i + ', ' + ((parent.wsrelays[i].state == 2) ? 'connected' : 'pending');
 | 
				
			||||||
                                if (parent.wsrelays[i].peer1 != null) {
 | 
					                                if (parent.wsrelays[i].peer1 != null) {
 | 
				
			||||||
                                    r += ', ' + cleanRemoteAddr(parent.wsrelays[i].peer1.req.clientIp);
 | 
					                                    r += ', ' + cleanRemoteAddr(parent.wsrelays[i].peer1.req.clientIp);
 | 
				
			||||||
                                    if (parent.wsrelays[i].peer1.user) { r += ' (User:' + parent.wsrelays[i].peer1.user.name + ')' }
 | 
					                                    if (parent.wsrelays[i].peer1.user) { r += ' (User:' + parent.wsrelays[i].peer1.user.name + ')' }
 | 
				
			||||||
| 
						 | 
					@ -1864,7 +1864,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
 | 
				
			||||||
            case 'adduserbatch':
 | 
					            case 'adduserbatch':
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var err = null;
 | 
					                    var err = null;
 | 
				
			||||||
                    
 | 
					
 | 
				
			||||||
                    // Add many new user accounts
 | 
					                    // Add many new user accounts
 | 
				
			||||||
                    if ((user.siteadmin & 2) == 0) { err = 'Access denied'; }
 | 
					                    if ((user.siteadmin & 2) == 0) { err = 'Access denied'; }
 | 
				
			||||||
                    else if ((domain.auth == 'sspi') || (domain.auth == 'ldap')) { err = 'Unable to create users when in SSPI or LDAP mode'; }
 | 
					                    else if ((domain.auth == 'sspi') || (domain.auth == 'ldap')) { err = 'Unable to create users when in SSPI or LDAP mode'; }
 | 
				
			||||||
| 
						 | 
					@ -1881,13 +1881,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
 | 
				
			||||||
                            userCount++;
 | 
					                            userCount++;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    
 | 
					
 | 
				
			||||||
                    // Handle any errors
 | 
					                    // Handle any errors
 | 
				
			||||||
                    if (err != null) {
 | 
					                    if (err != null) {
 | 
				
			||||||
                        if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'adduserbatch', responseid: command.responseid, result: err })); } catch (ex) { } }
 | 
					                        if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'adduserbatch', responseid: command.responseid, result: err })); } catch (ex) { } }
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    
 | 
					
 | 
				
			||||||
                    // Check if we exceed the maximum number of user accounts
 | 
					                    // Check if we exceed the maximum number of user accounts
 | 
				
			||||||
                    db.isMaxType(domain.limits.maxuseraccounts + userCount, 'user', domain.id, function (maxExceed) {
 | 
					                    db.isMaxType(domain.limits.maxuseraccounts + userCount, 'user', domain.id, function (maxExceed) {
 | 
				
			||||||
                        if (maxExceed) {
 | 
					                        if (maxExceed) {
 | 
				
			||||||
| 
						 | 
					@ -2249,7 +2249,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
 | 
				
			||||||
                        // We are not user group administrator, return a list with limited data for our domain.
 | 
					                        // We are not user group administrator, return a list with limited data for our domain.
 | 
				
			||||||
                        var groups = {}, groupCount = 0;
 | 
					                        var groups = {}, groupCount = 0;
 | 
				
			||||||
                        for (var i in parent.userGroups) { if (parent.userGroups[i].domain == domain.id) { groupCount++; groups[i] = { name: parent.userGroups[i].name }; } }
 | 
					                        for (var i in parent.userGroups) { if (parent.userGroups[i].domain == domain.id) { groupCount++; groups[i] = { name: parent.userGroups[i].name }; } }
 | 
				
			||||||
                        try { ws.send(JSON.stringify({ action: 'usergroups', ugroups: groupCount?groups:null, tag: command.tag })); } catch (ex) { }
 | 
					                        try { ws.send(JSON.stringify({ action: 'usergroups', ugroups: groupCount ? groups : null, tag: command.tag })); } catch (ex) { }
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        // We are user group administrator, return a full user group list for our domain.
 | 
					                        // We are user group administrator, return a full user group list for our domain.
 | 
				
			||||||
                        var groups = {}, groupCount = 0;
 | 
					                        var groups = {}, groupCount = 0;
 | 
				
			||||||
| 
						 | 
					@ -2750,14 +2750,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
 | 
				
			||||||
                                    chguser.passhint = hint;
 | 
					                                    chguser.passhint = hint;
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                                if (command.resetNextLogin === true) { chguser.passchange = -1; } else { chguser.passchange = Math.floor(Date.now() / 1000); }
 | 
					                                if (command.resetNextLogin === true) { chguser.passchange = -1; } else { chguser.passchange = Math.floor(Date.now() / 1000); }
 | 
				
			||||||
                                delete chguser.passtype; // Remove the password type if one was present.
 | 
					                                delete chguser.passtype;      // Remove the password type if one was present.
 | 
				
			||||||
                                if (command.removeMultiFactor == true) {
 | 
					                                if (command.removeMultiFactor === true) {
 | 
				
			||||||
                                    if (chguser.otpekey != null) { delete chguser.otpekey; }
 | 
					                                    delete chguser.otpkeys;   // One time backup codes
 | 
				
			||||||
                                    if (chguser.otpsecret != null) { delete chguser.otpsecret; }
 | 
					                                    delete chguser.otpsecret; // OTP Google Authenticator
 | 
				
			||||||
                                    if (chguser.otphkeys != null) { delete chguser.otphkeys; }
 | 
					                                    delete chguser.otphkeys;  // FIDO keys
 | 
				
			||||||
                                    if (chguser.otpkeys != null) { delete chguser.otpkeys; }
 | 
					                                    delete chguser.otpekey;   // Email 2FA
 | 
				
			||||||
                                    if ((chguser.otpekey != null) && (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null))) { delete chguser.otpekey; }
 | 
					                                    delete chguser.phone;     // SMS 2FA
 | 
				
			||||||
                                    if ((chguser.phone != null) && (parent.parent.smsserver != null)) { delete chguser.phone; }
 | 
					                                    delete chguser.otpdev;    // Push notification 2FA
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                                db.SetUser(chguser);
 | 
					                                db.SetUser(chguser);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3096,7 +3096,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Handle any errors
 | 
					                    // Handle any errors
 | 
				
			||||||
                    if (err != null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'editmesh', responseid: command.responseid, result: err })); } catch (ex) { } } break; }
 | 
					                    if (err != null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'editmesh', responseid: command.responseid, result: err })); } catch (ex) { } } break; }
 | 
				
			||||||
                    
 | 
					
 | 
				
			||||||
                    change = '';
 | 
					                    change = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Check if this user has rights to do this
 | 
					                    // Check if this user has rights to do this
 | 
				
			||||||
| 
						 | 
					@ -3126,7 +3126,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                                if (dup != null) {
 | 
					                                if (dup != null) {
 | 
				
			||||||
                                    // A duplicate was found, don't allow this change.
 | 
					                                    // A duplicate was found, don't allow this change.
 | 
				
			||||||
                                    displayNotificationMessage("Error, invite code \"" + dup + "\" already in use.", "Invite Codes", null, 6, 22, [ dup ]);
 | 
					                                    displayNotificationMessage("Error, invite code \"" + dup + "\" already in use.", "Invite Codes", null, 6, 22, [dup]);
 | 
				
			||||||
                                    return;
 | 
					                                    return;
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                                mesh.invite = { codes: command.invite.codes, flags: command.invite.flags };
 | 
					                                mesh.invite = { codes: command.invite.codes, flags: command.invite.flags };
 | 
				
			||||||
| 
						 | 
					@ -3366,7 +3366,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
 | 
				
			||||||
                                node.links[newuserid] = { rights: command.rights }
 | 
					                                node.links[newuserid] = { rights: command.rights }
 | 
				
			||||||
                                nodeChanged = true;
 | 
					                                nodeChanged = true;
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            
 | 
					
 | 
				
			||||||
                            // Save the user to the database
 | 
					                            // Save the user to the database
 | 
				
			||||||
                            if (newuserid.startsWith('user/')) {
 | 
					                            if (newuserid.startsWith('user/')) {
 | 
				
			||||||
                                db.SetUser(newuser);
 | 
					                                db.SetUser(newuser);
 | 
				
			||||||
| 
						 | 
					@ -4003,13 +4003,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
 | 
				
			||||||
                            command.nodeids = nodeids;
 | 
					                            command.nodeids = nodeids;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    } catch (ex) { console.log(ex); err = "Validation exception: " + ex; }
 | 
					                    } catch (ex) { console.log(ex); err = "Validation exception: " + ex; }
 | 
				
			||||||
                    
 | 
					
 | 
				
			||||||
                    // Handle any errors
 | 
					                    // Handle any errors
 | 
				
			||||||
                    if (err != null) {
 | 
					                    if (err != null) {
 | 
				
			||||||
                        if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'toast', responseid: command.responseid, result: err })); } catch (ex) { } }
 | 
					                        if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'toast', responseid: command.responseid, result: err })); } catch (ex) { } }
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    
 | 
					
 | 
				
			||||||
                    for (i in command.nodeids) {
 | 
					                    for (i in command.nodeids) {
 | 
				
			||||||
                        // Get the node and the rights for this node
 | 
					                        // Get the node and the rights for this node
 | 
				
			||||||
                        parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
 | 
					                        parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) {
 | 
				
			||||||
| 
						 | 
					@ -4361,7 +4361,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Check input
 | 
					                    // Check input
 | 
				
			||||||
                    if (typeof command.enabled != 'boolean') return;
 | 
					                    if (typeof command.enabled != 'boolean') return;
 | 
				
			||||||
                    
 | 
					
 | 
				
			||||||
                    // See if we really need to change the state
 | 
					                    // See if we really need to change the state
 | 
				
			||||||
                    if ((command.enabled === true) && (user.otpekey != null)) return;
 | 
					                    if ((command.enabled === true) && (user.otpekey != null)) return;
 | 
				
			||||||
                    if ((command.enabled === false) && (user.otpekey == null)) return;
 | 
					                    if ((command.enabled === false) && (user.otpekey == null)) return;
 | 
				
			||||||
| 
						 | 
					@ -4374,7 +4374,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
 | 
				
			||||||
                    // Notify change
 | 
					                    // Notify change
 | 
				
			||||||
                    var targets = ['*', 'server-users', user._id];
 | 
					                    var targets = ['*', 'server-users', user._id];
 | 
				
			||||||
                    if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
 | 
					                    if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
 | 
				
			||||||
                    var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msgid: command.enabled ? 88 : 89, msg: command.enabled ? "Enabled email two-factor authentication." :"Disabled email two-factor authentication.", domain: domain.id };
 | 
					                    var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msgid: command.enabled ? 88 : 89, msg: command.enabled ? "Enabled email two-factor authentication." : "Disabled email two-factor authentication.", domain: domain.id };
 | 
				
			||||||
                    if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come.
 | 
					                    if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come.
 | 
				
			||||||
                    parent.parent.DispatchEvent(targets, obj, event);
 | 
					                    parent.parent.DispatchEvent(targets, obj, event);
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
| 
						 | 
					@ -4588,6 +4588,49 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            case 'otpdev-clear':
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // Remove the authentication push notification device
 | 
				
			||||||
 | 
					                    if (user.otpdev != null) {
 | 
				
			||||||
 | 
					                        // Change the user
 | 
				
			||||||
 | 
					                        user.otpdev = obj.dbNodeKey;
 | 
				
			||||||
 | 
					                        parent.db.SetUser(user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Notify change
 | 
				
			||||||
 | 
					                        var targets = ['*', 'server-users', user._id];
 | 
				
			||||||
 | 
					                        if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
 | 
				
			||||||
 | 
					                        var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msgid: 114, msg: "Removed push notification authentication device", domain: domain.id };
 | 
				
			||||||
 | 
					                        if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come.
 | 
				
			||||||
 | 
					                        parent.parent.DispatchEvent(targets, obj, event);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            case 'otpdev-set':
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // Attempt to add a authentication push notification device
 | 
				
			||||||
 | 
					                    // This will only send a push notification to the device, the device needs to confirm for the auth device to be added.
 | 
				
			||||||
 | 
					                    if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
 | 
				
			||||||
 | 
					                    parent.GetNodeWithRights(domain, user, command.nodeid, function (node, rights, visible) {
 | 
				
			||||||
 | 
					                        // Only allow use of devices with full rights
 | 
				
			||||||
 | 
					                        if ((node == null) || (visible == false) || (rights != 0xFFFFFFFF) || (node.agent == null) || (node.agent.id != 14) || (node.pmt == null)) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Encode the cookie
 | 
				
			||||||
 | 
					                        const code = Buffer.from(user.name).toString('base64');
 | 
				
			||||||
 | 
					                        const authCookie = parent.parent.encodeCookie({ a: 'addAuth', c: code, u: user._id, n: node._id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Send out a push message to the device
 | 
				
			||||||
 | 
					                        var payload = { notification: { title: "MeshCentral", body: user.name + " authentication" }, data: { url: '2fa://auth?code=' + code + '&c=' + authCookie } };
 | 
				
			||||||
 | 
					                        var options = { priority: 'High', timeToLive: 60 }; // TTL: 1 minute
 | 
				
			||||||
 | 
					                        parent.parent.firebase.sendToDevice(node, payload, options, function (id, err, errdesc) {
 | 
				
			||||||
 | 
					                            if (err == null) {
 | 
				
			||||||
 | 
					                                parent.parent.debug('email', 'Successfully auth addition send push message to device ' + node.name);
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                parent.parent.debug('email', 'Failed auth addition push message to device ' + node.name + ', error: ' + errdesc);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            case 'webauthn-startregister':
 | 
					            case 'webauthn-startregister':
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -363,6 +363,7 @@
 | 
				
			||||||
                            <div id="manageEmail2FA"><div class="p2AccountActions"><span id="authEmailSetupCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_manageAuthEmail()">Manage email authentication</a><br /></span></div>
 | 
					                            <div id="manageEmail2FA"><div class="p2AccountActions"><span id="authEmailSetupCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_manageAuthEmail()">Manage email authentication</a><br /></span></div>
 | 
				
			||||||
                            <div id="manageAuthApp"><div class="p2AccountActions"><span id="authAppSetupCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_manageAuthApp()">Manage authenticator app</a><br /></span></div>
 | 
					                            <div id="manageAuthApp"><div class="p2AccountActions"><span id="authAppSetupCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_manageAuthApp()">Manage authenticator app</a><br /></span></div>
 | 
				
			||||||
                            <div id="manageHardwareOtp"><div class="p2AccountActions"><span id="authKeySetupCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_manageHardwareOtp(0)">Manage security keys</a><br /></span></div>
 | 
					                            <div id="manageHardwareOtp"><div class="p2AccountActions"><span id="authKeySetupCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_manageHardwareOtp(0)">Manage security keys</a><br /></span></div>
 | 
				
			||||||
 | 
					                            <div id="managePushAuthDev"><div class="p2AccountActions"><span id="authPushAuthDevCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_managePushAuthDev()">Manage push authentication</a><br /></span></div>
 | 
				
			||||||
                            <div id="manageOtp"><div class="p2AccountActions"><span id="authCodesSetupCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_manageOtp(0)">Manage backup codes</a><br /></span></div>
 | 
					                            <div id="manageOtp"><div class="p2AccountActions"><span id="authCodesSetupCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_manageOtp(0)">Manage backup codes</a><br /></span></div>
 | 
				
			||||||
                            <div class="p2AccountActions"></div><span><a href=# onclick="return account_viewPreviousLogins()">View previous logins</a><br /></span>
 | 
					                            <div class="p2AccountActions"></div><span><a href=# onclick="return account_viewPreviousLogins()">View previous logins</a><br /></span>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
| 
						 | 
					@ -2029,6 +2030,7 @@
 | 
				
			||||||
            QV('authEmailSetupCheck', (userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true));
 | 
					            QV('authEmailSetupCheck', (userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true));
 | 
				
			||||||
            QV('authAppSetupCheck', userinfo.otpsecret == 1);
 | 
					            QV('authAppSetupCheck', userinfo.otpsecret == 1);
 | 
				
			||||||
            QV('authKeySetupCheck', userinfo.otphkeys > 0);
 | 
					            QV('authKeySetupCheck', userinfo.otphkeys > 0);
 | 
				
			||||||
 | 
					            QV('authPushAuthDevCheck', (userinfo.otpdev > 0) && ((features2 & 2) != 0));
 | 
				
			||||||
            QV('authCodesSetupCheck', userinfo.otpkeys > 0);
 | 
					            QV('authCodesSetupCheck', userinfo.otpkeys > 0);
 | 
				
			||||||
            mainUpdate(4 + 128 + 4096);
 | 
					            mainUpdate(4 + 128 + 4096);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10052,6 +10054,31 @@
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function account_managePushAuthDev() {
 | 
				
			||||||
 | 
					            if (xxdialogMode || ((features2 & 2) == 0)) return;
 | 
				
			||||||
 | 
					            if (userinfo.otpdev == 1) {
 | 
				
			||||||
 | 
					                // Remove the 2FA device
 | 
				
			||||||
 | 
					                setDialogMode(2, "Authentication Device", 3, function () { meshserver.send({ action: 'otpdev-clear' }); }, "Confirm removal of push authentication device?");
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Create a list of all mobile devices
 | 
				
			||||||
 | 
					                var mobileDevices = [];
 | 
				
			||||||
 | 
					                for (var i in nodes) { var node = nodes[i]; if ((node.agent != null) && (node.agent.id == 14) && (node.pmt == 1) && (GetNodeRights(node) == 0xFFFFFFFF)) { mobileDevices.push(node); } }
 | 
				
			||||||
 | 
					                if (mobileDevices.length == 0) {
 | 
				
			||||||
 | 
					                    // No mobile devices found
 | 
				
			||||||
 | 
					                    setDialogMode(2, "Authentication Device", 1, null, "In order to use push notification authentication, a mobile device must be setup in your account with full rights.");
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // Set a 2FA device
 | 
				
			||||||
 | 
					                    var x = "Select a device to register for push notification authentication. Once selected, the device will prompt for confirmation." + '<br /><br />';
 | 
				
			||||||
 | 
					                    var y = '<select id=d2devselect style=width:240px>';
 | 
				
			||||||
 | 
					                    for (var i in mobileDevices) { y += '<option value="' + mobileDevices[i]._id + '">' + EscapeHtml(mobileDevices[i].name) + '</option>'; }
 | 
				
			||||||
 | 
					                    y += '</select>';
 | 
				
			||||||
 | 
					                    x += addHtmlValue("Device", y);
 | 
				
			||||||
 | 
					                    setDialogMode(2, "Authentication Device", 3, function () { meshserver.send({ action: 'otpdev-set', nodeid: Q('d2devselect').value }); }, x);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function account_manageHardwareOtp() {
 | 
					        function account_manageHardwareOtp() {
 | 
				
			||||||
            if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-hardware-manage')) { dialogclose(0); }
 | 
					            if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-hardware-manage')) { dialogclose(0); }
 | 
				
			||||||
            if (xxdialogMode || ((features & 4096) == 0)) return false;
 | 
					            if (xxdialogMode || ((features & 4096) == 0)) return false;
 | 
				
			||||||
| 
						 | 
					@ -11961,7 +11988,9 @@
 | 
				
			||||||
            109: "User login attempt on locked account from {0}, {1}, {2}",
 | 
					            109: "User login attempt on locked account from {0}, {1}, {2}",
 | 
				
			||||||
            110: "Invalid user login attempt from {0}, {1}, {2}",
 | 
					            110: "Invalid user login attempt from {0}, {1}, {2}",
 | 
				
			||||||
            111: "Device requested Intel(R) AMT ACM TLS activation, FQDN: {0}",
 | 
					            111: "Device requested Intel(R) AMT ACM TLS activation, FQDN: {0}",
 | 
				
			||||||
            112: "Ended messenger session \"{0}\" from {1} to {2}, {3} second(s)"
 | 
					            112: "Ended messenger session \"{0}\" from {1} to {2}, {3} second(s)",
 | 
				
			||||||
 | 
					            113: "Added push notification authentication device",
 | 
				
			||||||
 | 
					            114: "Removed push notification authentication device"
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Highlights the device being hovered
 | 
					        // Highlights the device being hovered
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6642,6 +6642,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
 | 
				
			||||||
        if ((typeof user2.otpsecret == 'string') && (user2.otpsecret != null)) { user2.otpsecret = 1; } // Indicates a time secret is present.
 | 
					        if ((typeof user2.otpsecret == 'string') && (user2.otpsecret != null)) { user2.otpsecret = 1; } // Indicates a time secret is present.
 | 
				
			||||||
        if ((typeof user2.otpkeys == 'object') && (user2.otpkeys != null)) { user2.otpkeys = 0; if (user.otpkeys != null) { for (var i = 0; i < user.otpkeys.keys.length; i++) { if (user.otpkeys.keys[i].u == true) { user2.otpkeys = 1; } } } } // Indicates the number of one time backup codes that are active.
 | 
					        if ((typeof user2.otpkeys == 'object') && (user2.otpkeys != null)) { user2.otpkeys = 0; if (user.otpkeys != null) { for (var i = 0; i < user.otpkeys.keys.length; i++) { if (user.otpkeys.keys[i].u == true) { user2.otpkeys = 1; } } } } // Indicates the number of one time backup codes that are active.
 | 
				
			||||||
        if ((typeof user2.otphkeys == 'object') && (user2.otphkeys != null)) { user2.otphkeys = user2.otphkeys.length; } // Indicates the number of hardware keys setup
 | 
					        if ((typeof user2.otphkeys == 'object') && (user2.otphkeys != null)) { user2.otphkeys = user2.otphkeys.length; } // Indicates the number of hardware keys setup
 | 
				
			||||||
 | 
					        if ((typeof user2.otpdev == 'string') && (user2.otpdev != null)) { user2.otpdev = 1; } // Indicates device for 2FA push notification
 | 
				
			||||||
        if ((typeof user2.webpush == 'object') && (user2.webpush != null)) { user2.webpush = user2.webpush.length; } // Indicates the number of web push sessions we have
 | 
					        if ((typeof user2.webpush == 'object') && (user2.webpush != null)) { user2.webpush = user2.webpush.length; } // Indicates the number of web push sessions we have
 | 
				
			||||||
        return user2;
 | 
					        return user2;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue