luci-mod-system: remplement password change as client side view
authorJo-Philipp Wich <jo@mein.io>
Mon, 16 Sep 2019 05:54:25 +0000 (07:54 +0200)
committerJo-Philipp Wich <jo@mein.io>
Mon, 16 Sep 2019 05:54:25 +0000 (07:54 +0200)
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
modules/luci-base/root/usr/share/rpcd/acl.d/luci-base.json
modules/luci-mod-system/htdocs/luci-static/resources/view/system/password.js
modules/luci-mod-system/luasrc/controller/admin/system.lua
modules/luci-mod-system/luasrc/view/admin_system/password.htm [deleted file]

index e28bdfa72768aa22d68270bc5dbe0e61cb5d8e1d..32cb10596bafc3a9b0d0955347ca5a175943509c 100644 (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": [ "*" ]
index 7a79d7e2da02b8b7870727a6e9870c9804bdab7f..6c5ffa1b26f994745573e25a1e823ecf956fc6f0 100644 (file)
@@ -1,31 +1,94 @@
-function submitPassword(ev) {
-       var pw1 = document.body.querySelector('[name="pw1"]'),
-           pw2 = document.body.querySelector('[name="pw2"]');
-
-       if (!pw1.value.length || !pw2.value.length)
-               return;
-
-       if (pw1.value === pw2.value) {
-               L.showModal(_('Change login password'),
-                       E('p', { class: 'spinning' }, _('Changing password…')));
-
-               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')))
-                               ]);
-
-                               pw1.value = pw2.value = '';
-                       });
-       }
-       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')))
-               ]);
+'use strict';
+'require form';
+'require rpc';
+
+var formData = {
+       password: {
+               pw1: null,
+               pw2: null
        }
-}
+};
+
+var callSetPassword = rpc.declare({
+       object: 'luci',
+       method: 'setPassword',
+       params: [ 'username', 'password' ],
+       expect: { result: false }
+});
+
+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");
+
+               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();
+                       });
+
+                       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
+});
index be00a3f678ef879b9089adf3ad4022e097b667b9..d1fda1d7c7331b61c089e1d325188eaa2ee00a3e 100644 (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
diff --git a/modules/luci-mod-system/luasrc/view/admin_system/password.htm b/modules/luci-mod-system/luasrc/view/admin_system/password.htm
deleted file mode 100644 (file)
index 6ca02a8..0000000
+++ /dev/null
@@ -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%>