mirror of
				https://github.com/Ylianst/MeshCentral.git
				synced 2025-03-09 15:40:18 +00:00 
			
		
		
		
	More work on SMS support.
This commit is contained in:
		
							parent
							
								
									19e276ca68
								
							
						
					
					
						commit
						200acde9c8
					
				
					 24 changed files with 287 additions and 14 deletions
				
			
		|  | @ -108,6 +108,7 @@ | |||
|     <Compile Include="meshaccelerator.js" /> | ||||
|     <Compile Include="meshctrl.js" /> | ||||
|     <Compile Include="meshmail.js" /> | ||||
|     <Compile Include="meshsms.js" /> | ||||
|     <Compile Include="meshscanner.js" /> | ||||
|     <Compile Include="certoperations.js" /> | ||||
|     <Compile Include="common.js" /> | ||||
|  |  | |||
|  | @ -280,7 +280,7 @@ function createMeshCore(agent) { | |||
|     */ | ||||
| 
 | ||||
|     // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
 | ||||
|     var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ('MeshCore CRC[' + crc32c(require('MeshAgent').coreHash) + ']') : ('MeshCore v6')), caps: 14 }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
 | ||||
|     var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ('MeshCore CRC-' + crc32c(require('MeshAgent').coreHash)) : ('MeshCore v6')), caps: 14 }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
 | ||||
| 
 | ||||
| 
 | ||||
|     // Get the operating system description string
 | ||||
|  | @ -1995,7 +1995,7 @@ function createMeshCore(agent) { | |||
|             var response = null; | ||||
|             switch (cmd) { | ||||
|                 case 'help': { // Displays available commands
 | ||||
|                     var fin = '', f = '', availcommands = 'startupoptions,alert,agentsize,version,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper'; | ||||
|                     var fin = '', f = '', availcommands = 'startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper'; | ||||
|                     if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration'; } | ||||
|                     if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; } | ||||
| 
 | ||||
|  | @ -2059,9 +2059,6 @@ function createMeshCore(agent) { | |||
|                         } else { response = "Agent Size: " + actualSize + " kb"; } | ||||
|                     } else { response = "Agent Size: " + actualSize + " kb"; } | ||||
|                     break; | ||||
|                 case 'version': | ||||
|                     response = "Mesh Agent Version: " + process.versions.meshAgent; | ||||
|                     break; | ||||
|                 case 'versions': | ||||
|                     response = JSON.stringify(process.versions, null, '  '); | ||||
|                     break; | ||||
|  |  | |||
							
								
								
									
										2
									
								
								emails/sms-messages.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								emails/sms-messages.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| [[0]] verification code is: [[1]] | ||||
| [[0]] access token is: [[1]] | ||||
							
								
								
									
										2
									
								
								emails/translations/sms-messages_cs.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								emails/translations/sms-messages_cs.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| [[0]] verification code is: [[1]] | ||||
| [[0]] access token is: [[1]] | ||||
							
								
								
									
										2
									
								
								emails/translations/sms-messages_de.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								emails/translations/sms-messages_de.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| [[0]] verification code is: [[1]] | ||||
| [[0]] access token is: [[1]] | ||||
							
								
								
									
										2
									
								
								emails/translations/sms-messages_es.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								emails/translations/sms-messages_es.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| [[0]] verification code is: [[1]] | ||||
| [[0]] access token is: [[1]] | ||||
							
								
								
									
										2
									
								
								emails/translations/sms-messages_fr.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								emails/translations/sms-messages_fr.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| [[0]] verification code is: [[1]] | ||||
| [[0]] access token is: [[1]] | ||||
							
								
								
									
										2
									
								
								emails/translations/sms-messages_hi.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								emails/translations/sms-messages_hi.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| [[0]] verification code is: [[1]] | ||||
| [[0]] access token is: [[1]] | ||||
							
								
								
									
										2
									
								
								emails/translations/sms-messages_ja.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								emails/translations/sms-messages_ja.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| [[0]] verification code is: [[1]] | ||||
| [[0]] access token is: [[1]] | ||||
							
								
								
									
										2
									
								
								emails/translations/sms-messages_ko.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								emails/translations/sms-messages_ko.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| [[0]] verification code is: [[1]] | ||||
| [[0]] access token is: [[1]] | ||||
							
								
								
									
										2
									
								
								emails/translations/sms-messages_nl.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								emails/translations/sms-messages_nl.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| [[0]] verification code is: [[1]] | ||||
| [[0]] access token is: [[1]] | ||||
							
								
								
									
										2
									
								
								emails/translations/sms-messages_pt.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								emails/translations/sms-messages_pt.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| [[0]] verification code is: [[1]] | ||||
| [[0]] access token is: [[1]] | ||||
							
								
								
									
										2
									
								
								emails/translations/sms-messages_ru.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								emails/translations/sms-messages_ru.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| [[0]] verification code is: [[1]] | ||||
| [[0]] access token is: [[1]] | ||||
							
								
								
									
										2
									
								
								emails/translations/sms-messages_zh-chs.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								emails/translations/sms-messages_zh-chs.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| [[0]] verification code is: [[1]] | ||||
| [[0]] access token is: [[1]] | ||||
|  | @ -30,6 +30,7 @@ function CreateMeshCentralServer(config, args) { | |||
|     obj.mqttbroker = null; | ||||
|     obj.swarmserver = null; | ||||
|     obj.mailserver = null; | ||||
|     obj.smsserver = null; | ||||
|     obj.amtEventHandler = null; | ||||
|     obj.pluginHandler = null; | ||||
|     obj.amtScanner = null; | ||||
|  | @ -1323,6 +1324,15 @@ function CreateMeshCentralServer(config, args) { | |||
|                     if (obj.args.lanonly == true) { addServerWarning("SMTP server has limited use in LAN mode."); } | ||||
|                 } | ||||
| 
 | ||||
|                 // Setup SMS gateway
 | ||||
|                 if (config.sms != null) { | ||||
|                     obj.smsserver = require('./meshsms.js').CreateMeshSMS(obj); | ||||
|                     if (obj.smsserver != null) { | ||||
|                         //obj.smsserver.verify();
 | ||||
|                         if (obj.args.lanonly == true) { addServerWarning("SMS gateway has limited use in LAN mode."); } | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 // Start periodic maintenance
 | ||||
|                 obj.maintenanceTimer = setInterval(obj.maintenanceActions, 1000 * 60 * 60); // Run this every hour
 | ||||
| 
 | ||||
|  | @ -2560,7 +2570,7 @@ function mainStart() { | |||
|         } | ||||
| 
 | ||||
|         // SMS support
 | ||||
|         if ((config.sms != null) && (config.sms.provider == 'twilio') && (typeof config.sms.sid == 'string') && (typeof config.sms.auth == 'string')) { modules.push('twilio'); } | ||||
|         if ((config.sms != null) && (config.sms.provider == 'twilio')) { modules.push('twilio'); } | ||||
| 
 | ||||
|         // Syslog support
 | ||||
|         if ((require('os').platform() != 'win32') && (config.settings.syslog || config.settings.syslogjson)) { modules.push('modern-syslog'); } | ||||
|  |  | |||
							
								
								
									
										10
									
								
								meshmail.js
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								meshmail.js
									
										
									
									
									
								
							|  | @ -160,7 +160,7 @@ module.exports.CreateMeshMail = function (parent) { | |||
| 
 | ||||
|                 var template = getTemplate('account-login', domain, language); | ||||
|                 if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null)) { | ||||
|                     parent.debug('email', "Error: Failed to get mail template."); // Not email template found
 | ||||
|                     parent.debug('email', "Error: Failed to get mail template."); // No email template found
 | ||||
|                     return;  | ||||
|                 } | ||||
| 
 | ||||
|  | @ -187,7 +187,7 @@ module.exports.CreateMeshMail = function (parent) { | |||
| 
 | ||||
|                 var template = getTemplate('account-invite', domain, language); | ||||
|                 if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null)) { | ||||
|                     parent.debug('email', "Error: Failed to get mail template."); // Not email template found
 | ||||
|                     parent.debug('email', "Error: Failed to get mail template."); // No email template found
 | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|  | @ -214,7 +214,7 @@ module.exports.CreateMeshMail = function (parent) { | |||
| 
 | ||||
|                 var template = getTemplate('account-check', domain, language); | ||||
|                 if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null)) { | ||||
|                     parent.debug('email', "Error: Failed to get mail template."); // Not email template found
 | ||||
|                     parent.debug('email', "Error: Failed to get mail template."); // No email template found
 | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|  | @ -242,7 +242,7 @@ module.exports.CreateMeshMail = function (parent) { | |||
| 
 | ||||
|                 var template = getTemplate('account-reset', domain, language); | ||||
|                 if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null)) { | ||||
|                     parent.debug('email', "Error: Failed to get mail template."); // Not email template found
 | ||||
|                     parent.debug('email', "Error: Failed to get mail template."); // No email template found
 | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|  | @ -270,7 +270,7 @@ module.exports.CreateMeshMail = function (parent) { | |||
| 
 | ||||
|                 var template = getTemplate('mesh-invite', domain, language); | ||||
|                 if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null)) { | ||||
|                     parent.debug('email', "Error: Failed to get mail template."); // Not email template found
 | ||||
|                     parent.debug('email', "Error: Failed to get mail template."); // No email template found
 | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										120
									
								
								meshsms.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								meshsms.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,120 @@ | |||
| /** | ||||
| * @description MeshCentral SMS gateway communication module | ||||
| * @author Ylian Saint-Hilaire | ||||
| * @copyright Intel Corporation 2018-2020 | ||||
| * @license Apache-2.0 | ||||
| * @version v0.0.1 | ||||
| */ | ||||
| 
 | ||||
| /*xjslint node: true */ | ||||
| /*xjslint plusplus: true */ | ||||
| /*xjslint maxlen: 256 */ | ||||
| /*jshint node: true */ | ||||
| /*jshint strict: false */ | ||||
| /*jshint esversion: 6 */ | ||||
| "use strict"; | ||||
| 
 | ||||
| // Construct a MeshAgent object, called upon connection
 | ||||
| module.exports.CreateMeshSMS = function (parent) { | ||||
|     var obj = {}; | ||||
|     obj.parent = parent; | ||||
|     obj.provider = null; | ||||
| 
 | ||||
|     // SMS gateway provider setup
 | ||||
|     switch (parent.config.sms.provider) { | ||||
|         case 'twilio': { | ||||
|             // Validate Twilio configuration values
 | ||||
|             if (typeof parent.config.sms.sid != 'string') { console.log('Invalid or missing SMS gateway provider sid.'); return null; } | ||||
|             if (typeof parent.config.sms.auth != 'string') { console.log('Invalid or missing SMS gateway provider auth.'); return null; } | ||||
|             if (typeof parent.config.sms.from != 'string') { console.log('Invalid or missing SMS gateway provider from.'); return null; } | ||||
| 
 | ||||
|             // Setup Twilio
 | ||||
|             var Twilio = require('twilio'); | ||||
|             obj.provider = new Twilio(parent.config.sms.sid, parent.config.sms.auth); | ||||
|             break; | ||||
|         } | ||||
|         default: { | ||||
|             // Unknown SMS gateway provider
 | ||||
|             console.log('Unknown SMS gateway provider: ' + parent.config.sms.provider); | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Send an SMS message
 | ||||
|     obj.sendSMS = function (to, msg, func) { | ||||
|         parent.debug('email', 'Sending SMS to: ' + to + ': ' + msg); | ||||
|         console.log({ from: parent.config.sms.from, to: to, body: msg }); | ||||
|         if (parent.config.sms.provider == 'twilio') { | ||||
|             obj.provider.messages.create({ | ||||
|                 from: parent.config.sms.from, | ||||
|                 to: to, | ||||
|                 body: msg | ||||
|             }, function (err, result) { | ||||
|                 if (err != null) { parent.debug('email', 'SMS error: ' + JSON.stringify(err)); } else { parent.debug('email', 'SMS result: ' + JSON.stringify(result)); } | ||||
|                 if (func != null) { func((err == null) && (result.status == 'queued'), err, result); } | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Get the correct SMS template
 | ||||
|     function getTemplate(templateNumber, domain, lang) { | ||||
|         parent.debug('email', 'Getting SMS template #' + templateNumber + ', lang: ' + lang); | ||||
|         if (Array.isArray(lang)) { lang = lang[0]; } // TODO: For now, we only use the first language given.
 | ||||
| 
 | ||||
|         var r = {}, emailsPath = null; | ||||
|         if ((domain != null) && (domain.webemailspath != null)) { emailsPath = domain.webemailspath; } | ||||
|         else if (obj.parent.webEmailsOverridePath != null) { emailsPath = obj.parent.webEmailsOverridePath; } | ||||
|         else if (obj.parent.webEmailsPath != null) { emailsPath = obj.parent.webEmailsPath; } | ||||
|         if ((emailsPath == null) || (obj.parent.fs.existsSync(emailsPath) == false)) { return null } | ||||
| 
 | ||||
|         // Get the non-english email if needed
 | ||||
|         var txtfile = null; | ||||
|         if ((lang != null) && (lang != 'en')) { | ||||
|             var translationsPath = obj.parent.path.join(emailsPath, 'translations'); | ||||
|             var translationsPathTxt = obj.parent.path.join(emailsPath, 'translations', 'sms-messages_' + lang + '.txt'); | ||||
|             if (obj.parent.fs.existsSync(translationsPath) && obj.parent.fs.existsSync(translationsPathTxt)) { | ||||
|                 txtfile = obj.parent.fs.readFileSync(translationsPathTxt).toString(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Get the english email
 | ||||
|         if ((htmlfile == null) || (txtfile == null)) { | ||||
|             var pathTxt = obj.parent.path.join(emailsPath, 'sms-messages.txt'); | ||||
|             if (obj.parent.fs.existsSync(pathTxt)) { | ||||
|                 txtfile = obj.parent.fs.readFileSync(pathTxt).toString(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // No email templates
 | ||||
|         if (txtfile == null) { return null; } | ||||
| 
 | ||||
|         // Decode the TXT file
 | ||||
|         lines = txtfile.split('\r\n').join('\n').split('\n') | ||||
|         if (lines.length >= templateNumber) return null; | ||||
| 
 | ||||
|         return lines[templateNumber]; | ||||
|     } | ||||
| 
 | ||||
|     // Send phone number verification SMS
 | ||||
|     obj.sendPhoneCheck = function (domain, phoneNumber, verificationCode, language, func) { | ||||
|         parent.debug('email', "Sending verification SMS to " + phoneNumber); | ||||
| 
 | ||||
|         var template = getTemplate(0, domain, language); | ||||
|         if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null)) { | ||||
|             parent.debug('email', "Error: Failed to get SMS template"); // No SMS template found
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Setup the template
 | ||||
|         template.split("[[0]]").join(domain.title ? domain.title : 'MeshCentral'); | ||||
|         template.split("[[1]]").join(verificationCode); | ||||
| 
 | ||||
|         // Send the SMS
 | ||||
|         obj.sendSMS(phoneNumber, template, func); | ||||
|     }; | ||||
| 
 | ||||
|     return obj; | ||||
| }; | ||||
| 
 | ||||
| // +18632703894
 | ||||
| // SMS 5032700426 "This is a test"
 | ||||
							
								
								
									
										63
									
								
								meshuser.js
									
										
									
									
									
								
							
							
						
						
									
										63
									
								
								meshuser.js
									
										
									
									
									
								
							|  | @ -755,6 +755,20 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use | |||
|                             } | ||||
|                             break; | ||||
|                         } | ||||
|                         case 'sms': { | ||||
|                             if (parent.parent.smsserver == null) { | ||||
|                                 r = "No SMS gateway in use."; | ||||
|                             } else { | ||||
|                                 if (cmdargs['_'].length != 2) { | ||||
|                                     r = "Usage: SMS \"PhoneNumber\" \"Message\"."; | ||||
|                                 } else { | ||||
|                                     parent.parent.smsserver.sendSMS(cmdargs['_'][0], cmdargs['_'][1], function (status) { | ||||
|                                         try { ws.send(JSON.stringify({ action: 'serverconsole', value: status?'Success':'Failed', tag: command.tag })); } catch (ex) { } | ||||
|                                     }); | ||||
|                                 } | ||||
|                             } | ||||
|                             break; | ||||
|                         } | ||||
|                         case 'le': { | ||||
|                             if (parent.parent.letsencrypt == null) { | ||||
|                                 r = "Let's Encrypt not in use."; | ||||
|  | @ -3695,6 +3709,55 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use | |||
|                     delete obj.hardwareKeyRegistrationRequest; | ||||
|                     break; | ||||
|                 } | ||||
|             case 'verifyPhone': { | ||||
|                 if (parent.parent.smsserver == null) return; | ||||
|                 if (common.validateString(command.phone, 1, 18) == false) break; // Check phone length
 | ||||
|                 if (command.phone.match(/^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/) == false) break; // Check phone
 | ||||
|                 var code = getRandomEightDigitInteger(); | ||||
|                 //console.log(code);
 | ||||
| 
 | ||||
|                 // TODO: We need to tie this cookie to this session and limit how many times we can guess the code
 | ||||
|                 const phoneCookie = parent.parent.encodeCookie({ a: 'verifyPhone', c: code, p: command.phone }); | ||||
| 
 | ||||
|                 ws.send(JSON.stringify({ action: 'verifyPhone', cookie: phoneCookie, success: true })); // DEBUG
 | ||||
|                 /* | ||||
|                 parent.parent.smsserver.sendPhoneCheck(domain, command.phone, code, parent.getLanguageCodes(req), function (success) { | ||||
|                     ws.send(JSON.stringify({ action: 'verifyPhone', cookie: phoneCookie, success: success })); | ||||
|                 }); | ||||
|                 */ | ||||
|                 break; | ||||
|             } | ||||
|             case 'confirmPhone': { | ||||
|                 if ((parent.parent.smsserver == null) || (typeof command.cookie != 'string') || (typeof command.code != 'number')) break; // Input checks
 | ||||
|                 var cookie = parent.parent.decodeCookie(command.cookie); | ||||
|                 if (cookie == null) break; // Invalid cookie
 | ||||
|                 if (cookie.c != command.code) { ws.send(JSON.stringify({ action: 'verifyPhone', cookie: command.cookie, success: true })); break; } // Code does not match
 | ||||
| 
 | ||||
|                 // Set the user's phone
 | ||||
|                 user.phone = cookie.p; | ||||
|                 db.SetUser(user); | ||||
| 
 | ||||
|                 // Event the change
 | ||||
|                 var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Verified phone number of user ' + EscapeHtml(user.name), 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(['*', 'server-users', user._id], obj, event); | ||||
| 
 | ||||
|                 break; | ||||
|             } | ||||
|             case 'removePhone': { | ||||
|                 if (user.phone == null) break; | ||||
| 
 | ||||
|                 // Clear the user's phone
 | ||||
|                 delete user.phone; | ||||
|                 db.SetUser(user); | ||||
| 
 | ||||
|                 // Event the change
 | ||||
|                 var event = { etype: 'user', userid: user._id, username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Removed phone number of user ' + EscapeHtml(user.name), 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(['*', 'server-users', user._id], obj, event); | ||||
| 
 | ||||
|                 break; | ||||
|             } | ||||
|             case 'getClip': { | ||||
|                 if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ | |||
|   ], | ||||
|   "dependencies": { | ||||
|     "archiver": "^3.0.0", | ||||
|     "archiver-zip-encrypted": "^1.0.8", | ||||
|     "body-parser": "^1.19.0", | ||||
|     "cbor": "^4.1.5", | ||||
|     "compression": "^1.7.4", | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								public/images/phone80.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/images/phone80.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.3 KiB | 
|  | @ -41,7 +41,8 @@ var meshCentralSourceFiles = [ | |||
|     "../emails/account-invite.txt", | ||||
|     "../emails/account-login.txt", | ||||
|     "../emails/account-reset.txt", | ||||
|     "../emails/mesh-invite.txt" | ||||
|     "../emails/mesh-invite.txt", | ||||
|     "../emails/sms-messages.txt" | ||||
| ]; | ||||
| 
 | ||||
| var minifyMeshCentralSourceFiles = [ | ||||
|  |  | |||
|  | @ -4846,7 +4846,6 @@ | |||
|     }, | ||||
|     { | ||||
|       "en": "Change Email Address", | ||||
|       "en": "E-mailadres wijzigen", | ||||
|       "xloc": [ | ||||
|         "login-mobile.handlebars->container->page_content->column_l->1->1->0->1->checkemailpanel->1->checkCheckOperations->1->2->1->1", | ||||
|         "login.handlebars->container->column_l->centralTable->1->0->logincell->checkemailpanel->1->checkCheckOperations->1->2->1->1" | ||||
|  | @ -9004,6 +9003,9 @@ | |||
|         "default.handlebars->29->889" | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "en": "E-mailadres wijzigen" | ||||
|     }, | ||||
|     { | ||||
|       "cs": "CHYBA: ", | ||||
|       "de": "FEHLER:", | ||||
|  | @ -27935,6 +27937,18 @@ | |||
|         "default.handlebars->29->1039" | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "en": "[[0]] access token is: [[1]]", | ||||
|       "xloc": [ | ||||
|         "sms-messages.txt" | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "en": "[[0]] verification code is: [[1]]", | ||||
|       "xloc": [ | ||||
|         "sms-messages.txt" | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "cs": "[[[SERVERNAME]]]", | ||||
|       "de": "[[[SERVERNAME]]]", | ||||
|  | @ -29682,4 +29696,4 @@ | |||
|       ] | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| } | ||||
|  | @ -306,6 +306,7 @@ | |||
|                     <div id="p2AccountSecurity" style="display:none"> | ||||
|                         <p><strong>Account security</strong></p> | ||||
|                         <div style="margin-left:25px"> | ||||
|                             <div id="managePhoneNumber"><div class="p2AccountActions"><span id="authPhoneNumberCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_managePhone()">Manage phone number</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="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> | ||||
|  | @ -1608,6 +1609,7 @@ | |||
| 
 | ||||
|             // Update account actions | ||||
|             QV('p2AccountSecurity', ((features & 4) == 0) && (serverinfo.domainauth == false) && ((features & 4096) != 0)); // Hide Account Security if in single user mode, domain authentication to 2 factor auth not supported. | ||||
|             QV('managePhoneNumber', features & 0x02000000); | ||||
|             QV('manageEmail2FA', features & 0x00800000); | ||||
|             QV('p2AccountPassActions', ((features & 4) == 0) && (serverinfo.domainauth == false)); // Hide Account Actions if in single user mode or domain authentication | ||||
|             //QV('p2AccountImage', ((features & 4) == 0) && (serverinfo.domainauth == false)); // If account actions are not visible, also remove the image on that panel | ||||
|  | @ -1679,6 +1681,7 @@ | |||
|             QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true)); | ||||
|             QV('verifyEmailId2', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true)); | ||||
|             QV('manageOtp', (userinfo.otpsecret == 1) || (userinfo.otphkeys > 0)); | ||||
|             QV('authPhoneNumberCheck', (userinfo.phone != null)); | ||||
|             QV('authEmailSetupCheck', (userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true)); | ||||
|             QV('authAppSetupCheck', userinfo.otpsecret == 1); | ||||
|             QV('authKeySetupCheck', userinfo.otphkeys > 0); | ||||
|  | @ -2182,6 +2185,16 @@ | |||
|                     }, 100); | ||||
|                     break; | ||||
|                 } | ||||
|                 case 'verifyPhone': { | ||||
|                     if (xxdialogMode && (xxdialogTag != 'verifyPhone')) return; | ||||
|                     var x = '<table><tr><td><img src="images/phone80.png" style=padding:8px>'; | ||||
|                     x += '<td>Check your phone and enter the verification code.'; | ||||
|                     x += '<br /><br /><div style=width:100%;text-align:center>' + "Verification code:" + ' <input type=tel pattern="[0-9]" inputmode="number" maxlength=8 id=d2phoneCodeInput onKeyUp=account_managePhoneCodeValidate() onkeypress="if (event.key==\'Enter\') account_managePhoneCodeValidate(1)"></div></table>'; | ||||
|                     setDialogMode(2, "Phone Notifications", 3, account_managePhoneConfirm, x, message.cookie); | ||||
|                     Q('d2phoneCodeInput').focus(); | ||||
|                     account_managePhoneCodeValidate(); | ||||
|                     break; | ||||
|                 } | ||||
|                 case 'event': { | ||||
|                     if (!message.event.nolog) { | ||||
|                         if (currentNode && (message.event.nodeid == currentNode._id)) { | ||||
|  | @ -7871,6 +7884,33 @@ | |||
|         // MY ACCOUNT | ||||
|         // | ||||
| 
 | ||||
|         function account_managePhone() { | ||||
|             if (xxdialogMode || ((features & 0x02000000) == 0)) return; | ||||
|             var x; | ||||
|             if (userinfo.phone != null) { | ||||
|                 x = '<table style=width:100%><tr><td style=width:56px><img src="images/phone80.png" style=padding:8px>'; | ||||
|                 x += '<td style=text-align:center><div style=padding:6px>' + "Verified phone number" + '</div><div style=font-size:20px>' + userinfo.phone + '</div>'; | ||||
|                 x += '<div style=margin:10px><label><input id=d2delPhone type=checkbox onclick=account_managePhoneRemoveValidate() />' + "Remove phone number" + '</label></div>'; | ||||
|                 setDialogMode(2, "Phone Notifications", 3, account_managePhoneRemove, x); | ||||
|                 account_managePhoneRemoveValidate(); | ||||
|             } else { | ||||
|                 x = '<table style=width:100%><tr><td style=width:56px><img src="images/phone80.png" style=padding:8px>'; | ||||
|                 x += '<td>Enter your SMS capable phone number. Once verified, the number may be used for login verification and other notifications.'; | ||||
|                 x += '<br /><br /><div style=width:100%;text-align:center>' + "Phone number:" + ' <input type=tel pattern="[0-9]{9}" autocomplete="tel" inputmode="tel" maxlength=18 id=d2phoneinput onKeyUp=account_managePhoneValidate() onkeypress="if (event.key==\'Enter\') account_managePhoneValidate(1)"></div></table>'; | ||||
|                 setDialogMode(2, "Phone Notifications", 3, account_managePhoneAdd, x, 'verifyPhone'); | ||||
|                 Q('d2phoneinput').focus(); | ||||
|                 account_managePhoneValidate(); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         function isPhoneNumber(x) { return x.match(/^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/) } | ||||
|         function account_managePhoneValidate(x) { var ok = isPhoneNumber(Q('d2phoneinput').value); QE('idx_dlgOkButton', ok); if ((x == 1) && ok) { dialogclose(1); } } | ||||
|         function account_managePhoneCodeValidate(x) { var ok = Q('d2phoneCodeInput').value.match(/[0-9]/); QE('idx_dlgOkButton', ok); if ((x == 1) && ok) { dialogclose(1); } } | ||||
|         function account_managePhoneConfirm(b, tag) { meshserver.send({ action: 'confirmPhone', code: parseInt(Q('d2phoneCodeInput').value), cookie: tag }); } | ||||
|         function account_managePhoneAdd() { if (isPhoneNumber(Q('d2phoneinput').value) == false) return; QE('d2phoneinput', false); meshserver.send({ action: 'verifyPhone', phone: Q('d2phoneinput').value }); } | ||||
|         function account_managePhoneRemove() { if (Q('d2delPhone').checked) { meshserver.send({ action: 'removePhone' }); } } | ||||
|         function account_managePhoneRemoveValidate() { QE('idx_dlgOkButton', Q('d2delPhone').checked); } | ||||
| 
 | ||||
|         function account_manageAuthEmail() { | ||||
|             if (xxdialogMode || ((features & 0x00800000) == 0)) return; | ||||
|             var emailU2Fenabled = ((userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true)); | ||||
|  | @ -10564,6 +10604,7 @@ | |||
|             } else { | ||||
|                 x += addDeviceAttribute("Email", everify + email + ' <a href=# style=cursor:pointer onclick=\'return doemail(event,\"' + user.email + '\")\'><img class=hoverButton src="images/link1.png" /></a>'); | ||||
|             } | ||||
|             if (user.phone != null) { x += addDeviceAttribute("Phone Number", user.phone); } | ||||
|             x += addDeviceAttribute("Server Rights", premsg + '<a href=# style=cursor:pointer onclick=\'return showUserAdminDialog(event,\"' + userid + '\")\'>' + msg.join(', ') + '</a>'); | ||||
|             if (user.quota) x += addDeviceAttribute("Server Quota", EscapeHtml(parseInt(user.quota) / 1024) + ' k'); | ||||
|             x += addDeviceAttribute("Creation", printDateTime(new Date(user.creation * 1000))); | ||||
|  |  | |||
|  | @ -1838,6 +1838,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { | |||
|             if (parent.mqttbroker != null) { features += 0x00400000; } // This server supports MQTT channels
 | ||||
|             if (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null)) { features += 0x00800000; } // using email for 2FA is allowed
 | ||||
|             if (domain.agentinvitecodes == true) { features += 0x01000000; } // Support for agent invite codes
 | ||||
|             if (parent.smsserver != null) { features += 0x02000000; } // SMS messaging is supported
 | ||||
| 
 | ||||
|             // Create a authentication cookie
 | ||||
|             const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id, ip: cleanRemoteAddr(req.ip) }, obj.parent.loginCookieEncryptionKey); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue