Improved web app, SSO fixes.

This commit is contained in:
Ylian Saint-Hilaire 2020-05-26 16:36:17 -07:00
parent 4efbfa89be
commit b80fe16325
11 changed files with 172 additions and 42 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

BIN
public/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

BIN
public/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

23
public/manifest.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "MeshCentral",
"short_name": "MeshCentral",
"description": "Open source web based, remote computer management.",
"scope": ".",
"start_url": "/",
"display": "standalone",
"orientation": "portrait",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"icons": [
{
"src": "android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}

View File

@ -2833,6 +2833,25 @@ a {
box-shadow: 0px 0px 15px #666; box-shadow: 0px 0px 15px #666;
} }
.suggestionBoxItem {
background-color:#5BB;
padding: 4px;
cursor: pointer;
margin-top: 2px;
margin-bottom: 2px;
border-radius: 4px;
}
.suggestionBoxItem:hover {
background-color:#4AA;
}
.suggestionBoxSubItem {
font-size: 11px;
margin-left: 4px;
color: #555;
}
.traceEvent { .traceEvent {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;

View File

@ -410,7 +410,7 @@
"ru": ", ", "ru": ", ",
"zh-chs": "", "zh-chs": "",
"xloc": [ "xloc": [
"default-mobile.handlebars->9->430", "default-mobile.handlebars->9->431",
"default.handlebars->25->1324" "default.handlebars->25->1324"
] ]
}, },
@ -687,7 +687,7 @@
"zh-chs": "1個字節", "zh-chs": "1個字節",
"xloc": [ "xloc": [
"default-mobile.handlebars->9->118", "default-mobile.handlebars->9->118",
"default-mobile.handlebars->9->434", "default-mobile.handlebars->9->435",
"default.handlebars->25->1384" "default.handlebars->25->1384"
] ]
}, },
@ -6285,7 +6285,7 @@
"ru": "Подтвердить удаление пользователя {0}?", "ru": "Подтвердить удаление пользователя {0}?",
"zh-chs": "確認刪除用戶{0}", "zh-chs": "確認刪除用戶{0}",
"xloc": [ "xloc": [
"default-mobile.handlebars->9->433" "default-mobile.handlebars->9->434"
] ]
}, },
{ {
@ -8154,7 +8154,7 @@
"ru": "Пользователь группы устройств", "ru": "Пользователь группы устройств",
"zh-chs": "設備組用戶", "zh-chs": "設備組用戶",
"xloc": [ "xloc": [
"default-mobile.handlebars->9->431", "default-mobile.handlebars->9->432",
"default.handlebars->25->1325" "default.handlebars->25->1325"
] ]
}, },
@ -19999,7 +19999,7 @@
"ru": "Права", "ru": "Права",
"zh-chs": "權限", "zh-chs": "權限",
"xloc": [ "xloc": [
"default-mobile.handlebars->9->429", "default-mobile.handlebars->9->430",
"default.handlebars->25->1323", "default.handlebars->25->1323",
"default.handlebars->25->1416" "default.handlebars->25->1416"
] ]
@ -21388,7 +21388,7 @@
"ru": "Удаленный пользователь Mesh", "ru": "Удаленный пользователь Mesh",
"zh-chs": "遠程網狀用戶", "zh-chs": "遠程網狀用戶",
"xloc": [ "xloc": [
"default-mobile.handlebars->9->432" "default-mobile.handlebars->9->433"
] ]
}, },
{ {
@ -27497,7 +27497,6 @@
"ru": "Пользователь", "ru": "Пользователь",
"zh-chs": "用戶", "zh-chs": "用戶",
"xloc": [ "xloc": [
"default-mobile.handlebars->9->428",
"default.handlebars->25->1205", "default.handlebars->25->1205",
"default.handlebars->25->1428", "default.handlebars->25->1428",
"default.handlebars->25->1536", "default.handlebars->25->1536",
@ -27654,6 +27653,12 @@
"default.handlebars->25->1655" "default.handlebars->25->1655"
] ]
}, },
{
"en": "User ID",
"xloc": [
"default-mobile.handlebars->9->429"
]
},
{ {
"cs": "Identifikátor uživatele", "cs": "Identifikátor uživatele",
"de": "Benutzeridentifikation", "de": "Benutzeridentifikation",
@ -27672,6 +27677,13 @@
"default.handlebars->25->1578" "default.handlebars->25->1578"
] ]
}, },
{
"en": "User Identifiers",
"xloc": [
"default.handlebars->25->1259",
"default.handlebars->25->1565"
]
},
{ {
"cs": "Export seznamu uživatelů", "cs": "Export seznamu uživatelů",
"de": "Benutzerlistenexport", "de": "Benutzerlistenexport",
@ -27703,6 +27715,7 @@
"ru": "Имя пользователя", "ru": "Имя пользователя",
"zh-chs": "用戶名", "zh-chs": "用戶名",
"xloc": [ "xloc": [
"default-mobile.handlebars->9->428",
"default.handlebars->25->1321" "default.handlebars->25->1321"
] ]
}, },
@ -27718,11 +27731,7 @@
"nl": "Gebruikersnamen", "nl": "Gebruikersnamen",
"pt": "Nomes de usuário", "pt": "Nomes de usuário",
"ru": "Имена пользователей", "ru": "Имена пользователей",
"zh-chs": "用戶名", "zh-chs": "用戶名"
"xloc": [
"default.handlebars->25->1259",
"default.handlebars->25->1565"
]
}, },
{ {
"cs": "Uživatelská oprávnění", "cs": "Uživatelská oprávnění",

View File

@ -1,12 +1,18 @@
<!DOCTYPE html> <!DOCTYPE html>
<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml"> <html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" /> <meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="format-detection" content="telephone=no" /> <meta name="format-detection" content="telephone=no" />
<link rel="shortcut icon" type="image/x-icon" href="{{{domainurl}}}favicon.ico" /> <link rel="manifest" href="{{{domainurl}}}manifest.json">
<link rel="shortcut icon" href="{{{domainurl}}}favicon.ico" />
<link rel="icon" type="image/png" sizes="16x16" href="{{{domainurl}}}favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{{domainurl}}}favicon-32x32.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="#ffffff">
<meta name="apple-mobile-web-app-title" content="{{{title}}}">
<script type="text/javascript" src="scripts/common-0.0.1{{{min}}}.js"></script> <script type="text/javascript" src="scripts/common-0.0.1{{{min}}}.js"></script>
<script type="text/javascript" src="scripts/meshcentral{{{min}}}.js"></script> <script type="text/javascript" src="scripts/meshcentral{{{min}}}.js"></script>
<script type="text/javascript" src="scripts/agent-redir-ws-0.1.1{{{min}}}.js"></script> <script type="text/javascript" src="scripts/agent-redir-ws-0.1.1{{{min}}}.js"></script>
@ -19,6 +25,8 @@
<script type="text/javascript" src="scripts/zlib-adler32{{{min}}}.js"></script> <script type="text/javascript" src="scripts/zlib-adler32{{{min}}}.js"></script>
<script type="text/javascript" src="scripts/zlib-crc32{{{min}}}.js"></script> <script type="text/javascript" src="scripts/zlib-crc32{{{min}}}.js"></script>
<script keeplink=1 type="text/javascript" src="scripts/filesaver.min.js"></script> <script keeplink=1 type="text/javascript" src="scripts/filesaver.min.js"></script>
<meta name="msapplication-TileColor" content="#00aba9">
<meta name="theme-color" content="#ffffff">
<title>{{{title}}}</title> <title>{{{title}}}</title>
<style> <style>
a { a {
@ -263,7 +271,7 @@
</div> </div>
<img id="topMenuIcon" class=noselect style="position:absolute;right:0;top:10px;bottom:50px;color:#c8c8c8;font-size:44px;margin-right:8px;cursor:pointer;display:none" onclick=topMenu() src="/images/3bars-30.png" width=30 height=30 /> <img id="topMenuIcon" class=noselect style="position:absolute;right:0;top:10px;bottom:50px;color:#c8c8c8;font-size:44px;margin-right:8px;cursor:pointer;display:none" onclick=topMenu() src="/images/3bars-30.png" width=30 height=30 />
</div> </div>
<div id=page_content style="overflow-y:scroll;position:absolute;bottom:32px;top:50px;width:100%"> <div id=page_content style="position:absolute;bottom:32px;top:50px;width:100%">
<div id=column_l style="width:100%;padding:0;position:absolute;bottom:0px;top:0px"> <div id=column_l style="width:100%;padding:0;position:absolute;bottom:0px;top:0px">
<div id=p0 style=display:none;width:100%;height:100%> <div id=p0 style=display:none;width:100%;height:100%>
<div style="display:flex;align-items:center;width:100%;height:100%"> <div style="display:flex;align-items:center;width:100%;height:100%">
@ -305,7 +313,7 @@
</td> </td>
</tr> </tr>
</table> </table>
<div id=p3info style="overflow-y:scroll;position:absolute;top:55px;bottom:0px;width:100%"> <div id=p3info style="overflow-y:auto;position:absolute;top:55px;bottom:0px;width:100%">
<div style="margin-left:8px"> <div style="margin-left:8px">
<div id="p3AccountActions"> <div id="p3AccountActions">
<div id="p2AccountSecurity" style="display:none"> <div id="p2AccountSecurity" style="display:none">
@ -357,7 +365,7 @@
</td> </td>
</tr> </tr>
</table> </table>
<div id=p5myfiles style="overflow-y:scroll;position:absolute;top:55px;bottom:0px;width:100%"> <div id=p5myfiles style="position:absolute;top:55px;bottom:0px;width:100%">
<table id="p5toolbar" style="width:100%;height:78px" cellpadding="0" cellspacing="0"> <table id="p5toolbar" style="width:100%;height:78px" cellpadding="0" cellspacing="0">
<tr> <tr>
<td style="width:100%;background-color:#d3d9d6;text-align:left;padding:4px" valign=bottom> <td style="width:100%;background-color:#d3d9d6;text-align:left;padding:4px" valign=bottom>
@ -397,7 +405,7 @@
</td> </td>
</tr> </tr>
</table> </table>
<div id="p5filetable" style="width:100%;height:calc(100% - 133px);overflow:auto;-webkit-user-select:none"> <div id="p5filetable" style="width:100%;height:calc(100% - 102px);overflow:auto;-webkit-user-select:none">
<!-- <!--
<div id="p5bigok" style="width:256px;overflow:hidden;position:absolute;left:337px;top:200px;text-align:center;font-size:1600%;color:#AAAAAA;display:none"><b>&checkmark;</b></div> <div id="p5bigok" style="width:256px;overflow:hidden;position:absolute;left:337px;top:200px;text-align:center;font-size:1600%;color:#AAAAAA;display:none"><b>&checkmark;</b></div>
<div id="p5bigfail" style="width:256px;overflow:hidden;position:absolute;left:337px;top:200px;text-align:center;font-size:1600%;color:#AAAAAA;display:none"><b>&#10007;</b></div> <div id="p5bigfail" style="width:256px;overflow:hidden;position:absolute;left:337px;top:200px;text-align:center;font-size:1600%;color:#AAAAAA;display:none"><b>&#10007;</b></div>
@ -486,7 +494,7 @@
</div> </div>
</div> </div>
</div> </div>
<div id=p10files style="overflow-y:scroll;position:absolute;top:55px;bottom:0px;width:100%;display:none"> <div id=p10files style="position:absolute;top:55px;bottom:0px;width:100%;display:none">
<table id="p13toolbar" style="width:100%;height:111px" cellpadding="0" cellspacing="0"> <table id="p13toolbar" style="width:100%;height:111px" cellpadding="0" cellspacing="0">
<tr> <tr>
<td style="background-color:#C0C0C0;border-bottom:2px solid black;padding:2px"> <td style="background-color:#C0C0C0;border-bottom:2px solid black;padding:2px">
@ -553,8 +561,8 @@
<div id=p10detailshtml style="margin-left:-3px"></div> <div id=p10detailshtml style="margin-left:-3px"></div>
</div> </div>
</div> </div>
<div id=p20 style=display:none;position:absolute;bottom:0;top:0;width:100%> <div id=p20 style="display:none;position:absolute;bottom:0;top:0;width:100%">
<table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;"> <table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;position:absolute;top:0">
<tr style=padding:0> <tr style=padding:0>
<td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()> <td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()>
<div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0"> <div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0">
@ -572,7 +580,9 @@
</td> </td>
</tr> </tr>
</table> </table>
<div id=p20info style="margin-left:8px;margin-right:8px"></div> <div style="overflow-y:auto;position:absolute;top:55px;bottom:0px;left:0px;right:0px">
<div id=p20info style="margin-left:8px;margin-right:8px"></div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -741,6 +751,9 @@
QV('manageEmail2FA', features & 0x00800000); QV('manageEmail2FA', features & 0x00800000);
QV('managePhoneNumber1', (features & 0x02000000) && (features & 0x04000000)); QV('managePhoneNumber1', (features & 0x02000000) && (features & 0x04000000));
QV('managePhoneNumber2', (features & 0x02000000) && !(features & 0x04000000)); QV('managePhoneNumber2', (features & 0x02000000) && !(features & 0x04000000));
attemptWebRTC = 0; // For now, default WebRTC off unless we set it in the URL.
if (args.webrtc != null) { attemptWebRTC = (args.webrtc == 1); }
} }
function onStateChanged(server, state, prevState, errorCode) { function onStateChanged(server, state, prevState, errorCode) {
@ -3779,7 +3792,7 @@
function p20showAddMeshUserDialog() { function p20showAddMeshUserDialog() {
if (xxdialogMode) return; if (xxdialogMode) return;
var x = addHtmlValue('User', '<input id=dp20username style=width:170px maxlength=32 onchange=p20validateAddMeshUserDialog() onkeyup=p20validateAddMeshUserDialog() />'); var x = addHtmlValue('User ID', '<input id=dp20username style=width:170px maxlength=256 onchange=p20validateAddMeshUserDialog() onkeyup=p20validateAddMeshUserDialog() />');
x += '<div style="border:2px groove gray;background-color:white;max-height:120px;overflow-y:scroll">'; x += '<div style="border:2px groove gray;background-color:white;max-height:120px;overflow-y:scroll">';
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20fulladmin>' + "Full Administrator" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20fulladmin>' + "Full Administrator" + '</label><br>';
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editmesh>' + "Edit Device Group" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editmesh>' + "Edit Device Group" + '</label><br>';
@ -3874,7 +3887,10 @@
if ((meshrights & 32768) != 0) r.push("Uninstall"); if ((meshrights & 32768) != 0) r.push("Uninstall");
} }
if (r.length == 0) { r.push("No Rights"); } if (r.length == 0) { r.push("No Rights"); }
var buttons = 1, x = addHtmlValue("User", EscapeHtml(decodeURIComponent(userid.split('/')[2]))); var buttons = 1, uname = userid.split('/')[2];
if (currentMesh.links[userid].name) { uname = currentMesh.links[userid].name; }
var x = addHtmlValue("User Name", EscapeHtml(uname));
if (uname != userid.split('/')[2]) { x += addHtmlValue("User ID", EscapeHtml(userid.split('/')[2])); }
x += addHtmlValue("Permissions", r.join(", ")); x += addHtmlValue("Permissions", r.join(", "));
if (((userinfo._id) != userid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) buttons += 4; if (((userinfo._id) != userid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) buttons += 4;
setDialogMode(2, "Device Group User", buttons, p20viewuserEx, x, userid); setDialogMode(2, "Device Group User", buttons, p20viewuserEx, x, userid);

View File

@ -8921,7 +8921,7 @@
x += '<br /><br />'; x += '<br /><br />';
} }
x += '<div style=\'position:relative\'>'; x += '<div style=\'position:relative\'>';
x += addHtmlValue("User Names", '<input id=dp20username style=width:230px maxlength=32 onchange=p20validateAddMeshUserDialog() onkeyup=p20validateAddMeshUserDialog() placeholder="user1, user2, user3" />'); x += addHtmlValue("User Identifiers", '<input id=dp20username style=width:230px maxlength=256 onchange=p20validateAddMeshUserDialog() onkeyup=p20validateAddMeshUserDialog() placeholder="user1, user2, user3" />');
x += '<div id=dp20usersuggest class=suggestionBox style=\'top:30px;left:130px;display:none\'></div>'; x += '<div id=dp20usersuggest class=suggestionBox style=\'top:30px;left:130px;display:none\'></div>';
x += '</div><br>'; x += '</div><br>';
} else if (userid == 1) { } else if (userid == 1) {
@ -9148,11 +9148,18 @@
if (lastuser.length > 0) { if (lastuser.length > 0) {
for (var i in users) { for (var i in users) {
if (users[i].name === lastuser) { exactMatch = true; break; } if (users[i].name === lastuser) { exactMatch = true; break; }
if (users[i].name.toLowerCase().indexOf(lastuserl) >= 0) { matchingUsers.push(users[i].name); if (matchingUsers.length >= 8) break; } if (users[i].name.toLowerCase().indexOf(lastuserl) >= 0) { matchingUsers.push([users[i]._id, users[i].name]); if (matchingUsers.length >= 8) break; }
} }
if ((exactMatch == false) && (matchingUsers.length > 0)) { if ((exactMatch == false) && (matchingUsers.length > 0)) {
var x = ''; var x = '';
for (var i in matchingUsers) { x += '<a href=# onclick=\'p20setname("' + encodeURIComponentEx(matchingUsers[i]) + '")\'>' + matchingUsers[i] + '</a><br />'; } for (var i in matchingUsers) {
var sid = matchingUsers[i][0], sname = matchingUsers[i][1];
if (sid.split('/')[2] == sname.toLowerCase()) {
x += '<div class=suggestionBoxItem onclick=\'p20setname("' + encodeURIComponentEx(sid.split('/')[2]) + '")\'>' + EscapeHtml(sname) + '</div>';
} else {
x += '<div class=suggestionBoxItem onclick=\'p20setname("' + encodeURIComponentEx(sid.split('/')[2]) + '")\'><div>' + EscapeHtml(sname) + '</div><div class=suggestionBoxSubItem>' + EscapeHtml(sid.split('/')[2]) + '</div></div>';
}
}
QH('dp20usersuggest', x); QH('dp20usersuggest', x);
showsuggestbox = true; showsuggestbox = true;
} }
@ -10795,7 +10802,7 @@
var x = "Allow users to manage this device group and devices in this group."; var x = "Allow users to manage this device group and devices in this group.";
if (features & 0x00080000) { x += " Users need to login to this server once before they can be added to a device group." } if (features & 0x00080000) { x += " Users need to login to this server once before they can be added to a device group." }
x += '<br /><br /><div style=\'position:relative\'>'; x += '<br /><br /><div style=\'position:relative\'>';
x += addHtmlValue("User Names", '<input id=dp51username style=width:230px maxlength=32 onchange=p51validateAddUserDialog() onkeyup=p51validateAddUserDialog() placeholder="user1, user2, user3" />'); x += addHtmlValue("User Identifiers", '<input id=dp51username style=width:230px maxlength=32 onchange=p51validateAddUserDialog() onkeyup=p51validateAddUserDialog() placeholder="user1, user2, user3" />');
x += '<div id=dp51usersuggest class=suggestionBox style=\'top:30px;left:130px;display:none\'></div>'; x += '<div id=dp51usersuggest class=suggestionBox style=\'top:30px;left:130px;display:none\'></div>';
x += '</div><br>'; x += '</div><br>';
setDialogMode(2, "Add Users to User Group", 3, p51showAddUserDialogEx, x); setDialogMode(2, "Add Users to User Group", 3, p51showAddUserDialogEx, x);
@ -10817,6 +10824,7 @@
function p51validateAddUserDialog() { function p51validateAddUserDialog() {
var meshrights = GetMeshRights(currentMesh); var meshrights = GetMeshRights(currentMesh);
var ok = true; var ok = true;
if (Q('dp51username')) { if (Q('dp51username')) {
var xusers = Q('dp51username').value.split(','); var xusers = Q('dp51username').value.split(',');
for (var i in xusers) { for (var i in xusers) {
@ -10831,11 +10839,18 @@
if (lastuser.length > 0) { if (lastuser.length > 0) {
for (var i in users) { for (var i in users) {
if (users[i].name === lastuser) { exactMatch = true; break; } if (users[i].name === lastuser) { exactMatch = true; break; }
if (users[i].name.toLowerCase().indexOf(lastuserl) >= 0) { matchingUsers.push(users[i].name); if (matchingUsers.length >= 8) break; } if (users[i].name.toLowerCase().indexOf(lastuserl) >= 0) { matchingUsers.push([users[i]._id, users[i].name]); if (matchingUsers.length >= 8) break; }
} }
if ((exactMatch == false) && (matchingUsers.length > 0)) { if ((exactMatch == false) && (matchingUsers.length > 0)) {
var x = ''; var x = '';
for (var i in matchingUsers) { x += '<a href=# onclick=\'p51setname("' + encodeURIComponentEx(matchingUsers[i]) + '")\'>' + matchingUsers[i] + '</a><br />'; } for (var i in matchingUsers) {
var sid = matchingUsers[i][0], sname = matchingUsers[i][1];
if (sid.split('/')[2] == sname.toLowerCase()) {
x += '<div class=suggestionBoxItem onclick=\'p51setname("' + encodeURIComponentEx(sid.split('/')[2]) + '")\'>' + EscapeHtml(sname) + '</div>';
} else {
x += '<div class=suggestionBoxItem onclick=\'p51setname("' + encodeURIComponentEx(sid.split('/')[2]) + '")\'><div>' + EscapeHtml(sname) + '</div><div class=suggestionBoxSubItem>' + EscapeHtml(sid.split('/')[2]) + '</div></div>';
}
}
QH('dp51usersuggest', x); QH('dp51usersuggest', x);
showsuggestbox = true; showsuggestbox = true;
} }
@ -10843,6 +10858,7 @@
} }
QV('dp51usersuggest', showsuggestbox); QV('dp51usersuggest', showsuggestbox);
} }
QE('idx_dlgOkButton', ok); QE('idx_dlgOkButton', ok);
} }

View File

@ -1,14 +1,22 @@
<!DOCTYPE html> <!DOCTYPE html>
<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml"> <html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" /> <meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="format-detection" content="telephone=no" /> <meta name="format-detection" content="telephone=no" />
<link rel="shortcut icon" type="image/x-icon" href="{{{domainurl}}}favicon.ico" /> <link rel="manifest" href="{{{domainurl}}}manifest.json">
<link rel="shortcut icon" href="{{{domainurl}}}favicon.ico" />
<link rel="icon" type="image/png" sizes="16x16" href="{{{domainurl}}}favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{{domainurl}}}favicon-32x32.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="#ffffff">
<meta name="apple-mobile-web-app-title" content="{{{title}}}">
<script type="text/javascript" src="scripts/common-0.0.1{{min}}.js"></script> <script type="text/javascript" src="scripts/common-0.0.1{{min}}.js"></script>
<script type="text/javascript" src="scripts/u2f-api{{min}}.js"></script> <script type="text/javascript" src="scripts/u2f-api{{min}}.js"></script>
<meta name="msapplication-TileColor" content="#00aba9">
<meta name="theme-color" content="#ffffff">
<title>{{{title}}} - Login</title> <title>{{{title}}} - Login</title>
<style> <style>
a { a {

View File

@ -4208,7 +4208,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Twitter // Twitter
if ((typeof domain.authstrategies.twitter == 'object') && (typeof domain.authstrategies.twitter.clientid == 'string') && (typeof domain.authstrategies.twitter.clientsecret == 'string')) { if ((typeof domain.authstrategies.twitter == 'object') && (typeof domain.authstrategies.twitter.clientid == 'string') && (typeof domain.authstrategies.twitter.clientsecret == 'string')) {
const TwitterStrategy = require('passport-twitter'); const TwitterStrategy = require('passport-twitter');
passport.use(new TwitterStrategy({ consumerKey: domain.authstrategies.twitter.clientid, consumerSecret: domain.authstrategies.twitter.clientsecret, callbackURL: url + 'auth-twitter-callback' }, var options = {
consumerKey: domain.authstrategies.twitter.clientid,
consumerSecret: domain.authstrategies.twitter.clientsecret,
callbackURL: (typeof domain.authstrategies.twitter.callbackurl == 'string') ? domain.authstrategies.twitter.callbackurl : (url + 'auth-twitter-callback')
};
parent.debug('web', 'Adding Twitter SSO with options: ' + JSON.stringify(options));
passport.use(new TwitterStrategy(options,
function (token, tokenSecret, profile, cb) { function (token, tokenSecret, profile, cb) {
parent.debug('web', 'Twitter profile: ' + JSON.stringify(profile)); parent.debug('web', 'Twitter profile: ' + JSON.stringify(profile));
var user = { sid: '~twitter:' + profile.id, name: profile.displayName, strategy: 'twitter' }; var user = { sid: '~twitter:' + profile.id, name: profile.displayName, strategy: 'twitter' };
@ -4239,7 +4245,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Google // Google
if ((typeof domain.authstrategies.google == 'object') && (typeof domain.authstrategies.google.clientid == 'string') && (typeof domain.authstrategies.google.clientsecret == 'string')) { if ((typeof domain.authstrategies.google == 'object') && (typeof domain.authstrategies.google.clientid == 'string') && (typeof domain.authstrategies.google.clientsecret == 'string')) {
const GoogleStrategy = require('passport-google-oauth20'); const GoogleStrategy = require('passport-google-oauth20');
passport.use(new GoogleStrategy({ clientID: domain.authstrategies.google.clientid, clientSecret: domain.authstrategies.google.clientsecret, callbackURL: url + 'auth-google-callback' }, var options = {
clientID: domain.authstrategies.google.clientid,
clientSecret: domain.authstrategies.google.clientsecret,
callbackURL: (typeof domain.authstrategies.google.callbackurl == 'string') ? domain.authstrategies.google.callbackurl : (url + 'auth-google-callback')
};
parent.debug('web', 'Adding Google SSO with options: ' + JSON.stringify(options));
passport.use(new GoogleStrategy(options,
function (token, tokenSecret, profile, cb) { function (token, tokenSecret, profile, cb) {
parent.debug('web', 'Google profile: ' + JSON.stringify(profile)); parent.debug('web', 'Google profile: ' + JSON.stringify(profile));
var user = { sid: '~google:' + profile.id, name: profile.displayName, strategy: 'google' }; var user = { sid: '~google:' + profile.id, name: profile.displayName, strategy: 'google' };
@ -4262,7 +4274,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Github // Github
if ((typeof domain.authstrategies.github == 'object') && (typeof domain.authstrategies.github.clientid == 'string') && (typeof domain.authstrategies.github.clientsecret == 'string')) { if ((typeof domain.authstrategies.github == 'object') && (typeof domain.authstrategies.github.clientid == 'string') && (typeof domain.authstrategies.github.clientsecret == 'string')) {
const GitHubStrategy = require('passport-github2'); const GitHubStrategy = require('passport-github2');
passport.use(new GitHubStrategy({ clientID: domain.authstrategies.github.clientid, clientSecret: domain.authstrategies.github.clientsecret, callbackURL: url + 'auth-github-callback' }, var options = {
clientID: domain.authstrategies.github.clientid,
clientSecret: domain.authstrategies.github.clientsecret,
callbackURL: (typeof domain.authstrategies.github.callbackurl == 'string') ? domain.authstrategies.github.callbackurl : (url + 'auth-github-callback')
};
parent.debug('web', 'Adding Github SSO with options: ' + JSON.stringify(options));
passport.use(new GitHubStrategy(options,
function (token, tokenSecret, profile, cb) { function (token, tokenSecret, profile, cb) {
parent.debug('web', 'Github profile: ' + JSON.stringify(profile)); parent.debug('web', 'Github profile: ' + JSON.stringify(profile));
var user = { sid: '~github:' + profile.id, name: profile.displayName, strategy: 'github' }; var user = { sid: '~github:' + profile.id, name: profile.displayName, strategy: 'github' };
@ -4285,7 +4303,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Reddit // Reddit
if ((typeof domain.authstrategies.reddit == 'object') && (typeof domain.authstrategies.reddit.clientid == 'string') && (typeof domain.authstrategies.reddit.clientsecret == 'string')) { if ((typeof domain.authstrategies.reddit == 'object') && (typeof domain.authstrategies.reddit.clientid == 'string') && (typeof domain.authstrategies.reddit.clientsecret == 'string')) {
const RedditStrategy = require('passport-reddit'); const RedditStrategy = require('passport-reddit');
passport.use(new RedditStrategy.Strategy({ clientID: domain.authstrategies.reddit.clientid, clientSecret: domain.authstrategies.reddit.clientsecret, callbackURL: url + 'auth-reddit-callback' }, var options = {
clientID: domain.authstrategies.reddit.clientid,
clientSecret: domain.authstrategies.reddit.clientsecret,
callbackURL: (typeof domain.authstrategies.reddit.callbackurl == 'string') ? domain.authstrategies.reddit.callbackurl : (url + 'auth-reddit-callback')
};
parent.debug('web', 'Adding Reddit SSO with options: ' + JSON.stringify(options));
passport.use(new RedditStrategy.Strategy(options,
function (token, tokenSecret, profile, cb) { function (token, tokenSecret, profile, cb) {
parent.debug('web', 'Reddit profile: ' + JSON.stringify(profile)); parent.debug('web', 'Reddit profile: ' + JSON.stringify(profile));
var user = { sid: '~reddit:' + profile.id, name: profile.name, strategy: 'reddit' }; var user = { sid: '~reddit:' + profile.id, name: profile.name, strategy: 'reddit' };
@ -4323,12 +4347,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Azure // Azure
if ((typeof domain.authstrategies.azure == 'object') && (typeof domain.authstrategies.azure.clientid == 'string') && (typeof domain.authstrategies.azure.clientsecret == 'string')) { if ((typeof domain.authstrategies.azure == 'object') && (typeof domain.authstrategies.azure.clientid == 'string') && (typeof domain.authstrategies.azure.clientsecret == 'string')) {
const AzureOAuth2Strategy = require('passport-azure-oauth2'); const AzureOAuth2Strategy = require('passport-azure-oauth2');
passport.use('azure', new AzureOAuth2Strategy({ var options = {
clientID: domain.authstrategies.azure.clientid, clientID: domain.authstrategies.azure.clientid,
clientSecret: domain.authstrategies.azure.clientsecret, clientSecret: domain.authstrategies.azure.clientsecret,
tenant: domain.authstrategies.azure.tenantid, tenant: domain.authstrategies.azure.tenantid,
callbackURL: url + 'auth-azure-callback' callbackURL: (typeof domain.authstrategies.azure.callbackurl == 'string') ? domain.authstrategies.azure.callbackurl : (url + 'auth-azure-callback')
}, };
parent.debug('web', 'Adding Azure SSO with options: ' + JSON.stringify(options));
passport.use('azure', new AzureOAuth2Strategy(options,
function (accessToken, refreshtoken, params, profile, done) { function (accessToken, refreshtoken, params, profile, done) {
var userex = null; var userex = null;
try { userex = require('jwt-simple').decode(params.id_token, "", true); } catch (ex) { } try { userex = require('jwt-simple').decode(params.id_token, "", true); } catch (ex) { }
@ -4377,7 +4403,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (cert == null) { if (cert == null) {
console.log('ERROR: Unable to read SAML IdP certificate: ' + domain.authstrategies.saml.cert); console.log('ERROR: Unable to read SAML IdP certificate: ' + domain.authstrategies.saml.cert);
} else { } else {
var options = { path: url + 'auth-saml-callback', entryPoint: domain.authstrategies.saml.idpurl, issuer: 'meshcentral' }; var options = {
path: (typeof domain.authstrategies.saml.callbackurl == 'string') ? domain.authstrategies.saml.callbackurl : (url + 'auth-saml-callback'),
entryPoint: domain.authstrategies.saml.idpurl, issuer: 'meshcentral'
};
parent.debug('web', 'Adding SAML SSO with options: ' + JSON.stringify(options));
if (typeof domain.authstrategies.saml.entityid == 'string') { options.issuer = domain.authstrategies.saml.entityid; } if (typeof domain.authstrategies.saml.entityid == 'string') { options.issuer = domain.authstrategies.saml.entityid; }
options.cert = cert.toString().split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join(''); options.cert = cert.toString().split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('');
const SamlStrategy = require('passport-saml').Strategy; const SamlStrategy = require('passport-saml').Strategy;
@ -4414,7 +4444,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (cert == null) { if (cert == null) {
console.log('ERROR: Unable to read Intel SAML IdP certificate: ' + domain.authstrategies.intel.cert); console.log('ERROR: Unable to read Intel SAML IdP certificate: ' + domain.authstrategies.intel.cert);
} else { } else {
var options = { path: url + 'auth-intel-callback', entryPoint: domain.authstrategies.intel.idpurl, issuer: 'meshcentral' }; var options = {
path: (typeof domain.authstrategies.intel.callbackurl == 'string') ? domain.authstrategies.intel.callbackurl : (url + 'auth-intel-callback'),
entryPoint: domain.authstrategies.intel.idpurl, issuer: 'meshcentral'
};
parent.debug('web', 'Adding Intel SSO with options: ' + JSON.stringify(options));
if (typeof domain.authstrategies.intel.entityid == 'string') { options.issuer = domain.authstrategies.intel.entityid; } if (typeof domain.authstrategies.intel.entityid == 'string') { options.issuer = domain.authstrategies.intel.entityid; }
options.cert = cert.toString().split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join(''); options.cert = cert.toString().split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('');
const SamlStrategy = require('passport-saml').Strategy; const SamlStrategy = require('passport-saml').Strategy;
@ -4453,7 +4487,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (cert == null) { if (cert == null) {
console.log('ERROR: Unable to read JumpCloud IdP certificate: ' + domain.authstrategies.jumpcloud.cert); console.log('ERROR: Unable to read JumpCloud IdP certificate: ' + domain.authstrategies.jumpcloud.cert);
} else { } else {
var options = { path: url + 'auth-jumpcloud-callback', entryPoint: domain.authstrategies.jumpcloud.idpurl, issuer: 'meshcentral' }; var options = {
path: (typeof domain.authstrategies.jumpcloud.callbackurl == 'string') ? domain.authstrategies.jumpcloud.callbackurl : (url + 'auth-jumpcloud-callback'),
entryPoint: domain.authstrategies.jumpcloud.idpurl, issuer: 'meshcentral'
};
parent.debug('web', 'Adding JumpCloud SSO with options: ' + JSON.stringify(options));
if (typeof domain.authstrategies.jumpcloud.entityid == 'string') { options.issuer = domain.authstrategies.jumpcloud.entityid; } if (typeof domain.authstrategies.jumpcloud.entityid == 'string') { options.issuer = domain.authstrategies.jumpcloud.entityid; }
options.cert = cert.toString().split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join(''); options.cert = cert.toString().split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('');
const SamlStrategy = require('passport-saml').Strategy; const SamlStrategy = require('passport-saml').Strategy;
@ -5368,6 +5406,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Return the correct render page given mobile, minify and override path. // Return the correct render page given mobile, minify and override path.
function getRenderPage(pagename, req, domain) { function getRenderPage(pagename, req, domain) {
var mobile = isMobileBrowser(req), minify = (domain.minify == true), p; var mobile = isMobileBrowser(req), minify = (domain.minify == true), p;
if (req.query.mobile == '1') { mobile = true; } else if (req.query.mobile == '0') { mobile = false; }
if (req.query.minify == '1') { minify = true; } else if (req.query.minify == '0') { minify = false; } if (req.query.minify == '1') { minify = true; } else if (req.query.minify == '0') { minify = false; }
if (mobile) { if (mobile) {
if ((domain != null) && (domain.webviewspath != null)) { // If the domain has a web views path, use that first if ((domain != null) && (domain.webviewspath != null)) { // If the domain has a web views path, use that first