luci-mod-system: remplement password change as client side view

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich 2019-09-16 07:54:25 +02:00
parent a31d1d10e0
commit 9ae9657a85
4 changed files with 91 additions and 99 deletions

View file

@ -50,7 +50,7 @@
"ubus": {
"file": [ "write", "remove" ],
"iwinfo": [ "scan" ],
"luci": [ "setInitAction", "setLocaltime" ],
"luci": [ "setInitAction", "setLocaltime", "setPassword" ],
"uci": [ "add", "apply", "confirm", "delete", "order", "set", "rename" ]
},
"uci": [ "*" ]

View file

@ -1,31 +1,94 @@
function submitPassword(ev) {
var pw1 = document.body.querySelector('[name="pw1"]'),
pw2 = document.body.querySelector('[name="pw2"]');
'use strict';
'require form';
'require rpc';
if (!pw1.value.length || !pw2.value.length)
return;
var formData = {
password: {
pw1: null,
pw2: null
}
};
if (pw1.value === pw2.value) {
L.showModal(_('Change login password'),
E('p', { class: 'spinning' }, _('Changing password…')));
var callSetPassword = rpc.declare({
object: 'luci',
method: 'setPassword',
params: [ 'username', 'password' ],
expect: { result: false }
});
L.post('admin/system/admin/password/json', { password: pw1.value },
function() {
showModal(_('Change login password'), [
E('div', _('The system password has been successfully changed.')),
E('div', { 'class': 'right' },
E('div', { class: 'btn', click: L.hideModal }, _('Dismiss')))
]);
return L.view.extend({
checkPassword: function(section_id, value) {
var strength = document.querySelector('.cbi-value-description'),
strongRegex = new RegExp("^(?=.{8,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*\\W).*$", "g"),
mediumRegex = new RegExp("^(?=.{7,})(((?=.*[A-Z])(?=.*[a-z]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[0-9]))).*$", "g"),
enoughRegex = new RegExp("(?=.{6,}).*", "g");
pw1.value = pw2.value = '';
if (strength && value.length) {
if (false == enoughRegex.test(value))
strength.innerHTML = '%s: <span style="color:red">%s</span>'.format(_('Password strength'), _('More Characters'));
else if (strongRegex.test(value))
strength.innerHTML = '%s: <span style="color:green">%s</span>'.format(_('Password strength'), _('Strong'));
else if (mediumRegex.test(value))
strength.innerHTML = '%s: <span style="color:orange">%s</span>'.format(_('Password strength'), _('Medium'));
else
strength.innerHTML = '%s: <span style="color:red">%s</span>'.format(_('Password strength'), _('Weak'));
}
return true;
},
render: function() {
var m, s, o;
m = new form.JSONMap(formData, _('Router Password'), _('Changes the administrator password for accessing the device'));
s = m.section(form.NamedSection, 'password', 'password');
o = s.option(form.Value, 'pw1', _('Password'));
o.password = true;
o.validate = this.checkPassword;
o = s.option(form.Value, 'pw2', _('Confirmation'), ' ');
o.password = true;
o.renderWidget = function(/* ... */) {
var node = form.Value.prototype.renderWidget.apply(this, arguments);
node.childNodes[1].addEventListener('keydown', function(ev) {
if (ev.keyCode == 13 && !ev.currentTarget.classList.contains('cbi-input-invalid'))
document.querySelector('.cbi-button-save').click();
});
}
else {
L.showModal(_('Change login password'), [
E('div', { class: 'alert-message warning' },
_('Given password confirmation did not match, password not changed!')),
E('div', { 'class': 'right' },
E('div', { class: 'btn', click: L.hideModal }, _('Dismiss')))
]);
}
}
return node;
};
return m.render();
},
handleSave: function() {
var map = document.querySelector('.cbi-map');
return L.dom.callClassMethod(map, 'save').then(function() {
if (formData.password.pw1 == null || formData.password.pw1.length == 0)
return;
if (formData.password.pw1 != formData.password.pw2) {
L.ui.addNotification(null, E('p', _('Given password confirmation did not match, password not changed!')), 'danger');
return;
}
return callSetPassword('root', formData.password.pw1).then(function(success) {
if (success)
L.ui.addNotification(null, E('p', _('The system password has been successfully changed.')), 'info');
else
L.ui.addNotification(null, E('p', _('Failed to change the system password.')), 'danger');
formData.password.pw1 = null;
formData.password.pw2 = null;
L.dom.callClassMethod(map, 'render');
});
});
},
handleSaveApply: null,
handleReset: null
});

View file

@ -12,8 +12,7 @@ function index()
entry({"admin", "system", "ntp_restart"}, call("action_ntp_restart"), nil).leaf = true
entry({"admin", "system", "admin"}, firstchild(), _("Administration"), 2)
entry({"admin", "system", "admin", "password"}, template("admin_system/password"), _("Router Password"), 1)
entry({"admin", "system", "admin", "password", "json"}, post("action_password"))
entry({"admin", "system", "admin", "password"}, view("system/password"), _("Router Password"), 1)
if fs.access("/etc/config/dropbear") then
entry({"admin", "system", "admin", "dropbear"}, cbi("admin_system/dropbear"), _("SSH Access"), 2)
@ -281,17 +280,6 @@ function action_reset()
http.redirect(luci.dispatcher.build_url('admin/system/flashops'))
end
function action_password()
local password = luci.http.formvalue("password")
if not password then
luci.http.status(400, "Bad Request")
return
end
luci.http.prepare_content("application/json")
luci.http.write_json({ code = luci.sys.user.setpasswd("root", password) })
end
function action_reboot()
luci.sys.reboot()
end

View file

@ -1,59 +0,0 @@
<%+header%>
<input type="password" aria-hidden="true" style="position:absolute; left:-10000px" />
<script type="text/javascript">
function checkPassword() {
var pw1 = document.body.querySelector('[name="pw1"]');
var view = document.getElementById("passstrength");
var strongRegex = new RegExp("^(?=.{8,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*\\W).*$", "g");
var mediumRegex = new RegExp("^(?=.{7,})(((?=.*[A-Z])(?=.*[a-z]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[0-9]))).*$", "g");
var enoughRegex = new RegExp("(?=.{6,}).*", "g");
if (false == enoughRegex.test(pw1.value)) {
view.innerHTML = '<%:Password strength%>: <span style="color:red"><%:More Characters%></span>';
} else if (strongRegex.test(pw1.value)) {
view.innerHTML = '<%:Password strength%>: <span style="color:green"><%:Strong%></span>';
} else if (mediumRegex.test(pw1.value)) {
view.innerHTML = '<%:Password strength%>: <span style="color:orange"><%:Medium%></span>';
} else {
view.innerHTML = '<%:Password strength%>: <span style="color:red"><%:Weak%></span>';
}
return true;
}
</script>
<div class="cbi-map">
<h2><%:Router Password%></h2>
<div class="cbi-section-descr">
<%:Changes the administrator password for accessing the device%>
</div>
<div class="cbi-section-node">
<div class="cbi-value">
<label class="cbi-value-title" for="image"><%:Password%></label>
<div class="cbi-value-field">
<input type="password" name="pw1" onkeyup="checkPassword()"/><!--
--><button class="cbi-button cbi-button-neutral" title="<%:Reveal/hide password%>" aria-label="<%:Reveal/hide password%>" onclick="var e = this.previousElementSibling; e.type = (e.type === 'password') ? 'text' : 'password'"></button>
</div>
</div>
<div class="cbi-value">
<label class="cbi-value-title" for="image"><%:Confirmation%></label>
<div class="cbi-value-field">
<input type="password" name="pw2" onkeydown="if (event.keyCode === 13) submitPassword(event)" /><!--
--><button class="cbi-button cbi-button-neutral" title="<%:Reveal/hide password%>" aria-label="<%:Reveal/hide password%>" onclick="var e = this.previousElementSibling; e.type = (e.type === 'password') ? 'text' : 'password'"></button>
<div id="passstrength" class="cbi-value-description"></div>
</div>
</div>
</div>
</div>
<div class="cbi-page-actions">
<button class="btn cbi-button-apply" onclick="submitPassword(event)"><%:Save%></button>
</div>
<script type="application/javascript" src="<%=resource%>/view/system/password.js"></script>
<%+footer%>