luci-mod-system: unify password change
authorChristian Korber <redacted>
Fri, 24 Apr 2026 13:19:26 +0000 (15:19 +0200)
committerPaul Donald <redacted>
Wed, 3 Jun 2026 10:49:07 +0000 (13:49 +0300)
A flag is added to choose if Password of rpcd user is changed or unix
user.
If rpc is chosen the old password has to be specified.

Signed-off-by: Christian Korber <redacted>
modules/luci-mod-system/htdocs/luci-static/resources/view/system/password.js
modules/luci-mod-system/root/usr/share/rpcd/acl.d/luci-mod-system.json

index eb9d4a6d76338c1e4eb320e235e597e1575f989f..91bbd3161d24b88c87592efbf4379a2598d379db 100644 (file)
@@ -2,11 +2,17 @@
 'require view';
 'require dom';
 'require ui';
+'require fs';
+'require uci';
 'require form';
 'require rpc';
 
 var formData = {
-       password: {
+       data: {
+               rpc_user: null,
+               user: null,
+               rpcd: null,
+               oldpw: null,
                pw1: null,
                pw2: null
        }
@@ -15,8 +21,8 @@ var formData = {
 var callSetPassword = rpc.declare({
        object: 'luci',
        method: 'setPassword',
-       params: [ 'username', 'password' ],
-       expect: { result: false }
+       params: [ 'username', 'password', 'oldpassword', 'rpcd' ],
+       expect: { result: 1 }
 });
 
 return view.extend({
@@ -40,13 +46,66 @@ return view.extend({
                return true;
        },
 
-       render: function() {
-               var m, s, o;
+       load: function() {
+               return Promise.all([
+                       L.resolveDefault(fs.stat('/usr/sbin/uhttpd'), null),
+                       fs.lines('/etc/passwd'),
+                       uci.load('rpcd')
+               ]);
+       },
+
+       render: function([has_uhttpd, passwd]) {
+               var m, s, o, rpcd;
+
+               const known_unix_users = {};
 
-               m = new form.JSONMap(formData, _('Router Password'), _('Changes the administrator password for accessing the device'));
+               for (let p of passwd) {
+                       const parts = p.split(/:/);
+
+                       if (parts.length >= 7)
+                               known_unix_users[parts[0]] = true;
+               }
+
+               m = new form.JSONMap(formData, _('Password'), _('Changes the password for accessing the device as (rpcd) user'));
                m.readonly = !L.hasViewPermission();
 
-               s = m.section(form.NamedSection, 'password', 'password');
+               s = m.section(form.NamedSection, 'data', 'data');
+
+               if (has_uhttpd) {
+                       let logins = [];
+
+                       uci.sections('rpcd', 'login', s => logins.push(s.username));
+
+                       rpcd = s.option(form.Flag, 'rpcd', _('Change password for rpcd user'));
+                       rpcd.default = false;
+
+                       o = s.option(form.Value, 'rpc_user', _('rpcd username'));
+                       for (let user of logins) {
+                               if (user == 'root')
+                                       continue;
+
+                               o.value(user, _('%s').format(user));
+                       }
+                       o.rmempty = false;
+                       o.depends({ 'rpcd': '1' });
+
+                       o = s.option(form.Value, 'user', _('Router username'));
+                       for (let user in known_unix_users)
+                               o.value(user, _('%s').format(user));
+                       o.rmempty = false;
+                       o.depends({ 'rpcd': '0' });
+
+                       o = s.option(form.Value, 'oldpw', _('Old Password'));
+                       o.password = true;
+                       o.depends({ 'rpcd': '1' });
+               }
+
+               if (!has_uhttpd) {
+                       o = s.option(form.Value, 'user', _('Router username'));
+                       for (let user in known_unix_users)
+                               o.value(user);
+                       o.rmempty = false;
+               }
 
                o = s.option(form.Value, 'pw1', _('Password'));
                o.password = true;
@@ -72,22 +131,39 @@ return view.extend({
                var map = document.querySelector('.cbi-map');
 
                return dom.callClassMethod(map, 'save').then(function() {
-                       if (formData.password.pw1 == null || formData.password.pw1.length == 0)
+                       let rpc_user = formData.data.rpc_user;
+                       let user = formData.data.user;
+                       let rpcd = formData.data.rpcd;
+                       let oldpw = formData.data.oldpw;
+
+                       if (rpc_user && (oldpw == null || oldpw.length == 0))
+                               return;
+
+                       if (formData.data.pw1 == null || formData.data.pw1.length == 0)
                                return;
 
-                       if (formData.password.pw1 != formData.password.pw2) {
+                       if (formData.data.pw1 != formData.data.pw2) {
                                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) {
+                       return callSetPassword(
+                               rpc_user ? rpc_user : user,
+                               formData.data.pw1,
+                               oldpw ? oldpw : '',
+                               rpcd ? true : false,
+                       ).then(function(success) {
                                if (success)
                                        ui.addNotification(null, E('p', _('The system password has been successfully changed.')), 'info');
                                else
                                        ui.addNotification(null, E('p', _('Failed to change the system password.')), 'danger');
 
-                               formData.password.pw1 = null;
-                               formData.password.pw2 = null;
+                               formData.data.rpc_user = null;
+                               formData.data.user = null;
+                               formData.data.rpcd = null;
+                               formData.data.pw1 = null;
+                               formData.data.pw2 = null;
+                               formData.data.oldpw = null;
 
                                dom.callClassMethod(map, 'render');
                        });
index fc633f475084a28526c4d75b206181e7865e896e..55de8503256b9679f0b1ac2ee8c0fbb42a81af2b 100644 (file)
@@ -2,13 +2,20 @@
        "luci-mod-system-config": {
                "description": "Grant access to system configuration",
                "read": {
+                       "cgi-io": [ "read" ],
+                       "file": {
+                               "/etc/passwd": [ "read" ]
+                       },
                        "ubus": {
                                "luci": [ "getLEDs", "getTimezones", "getUSBDevices", "getUnixtime" ],
                                "rc": [ "list" ]
                        },
-                       "uci": [ "luci", "system" ]
+                       "uci": [ "luci", "system", "rpcd" ]
                },
                "write": {
+                       "file": {
+                               "/usr/sbin/uhttpd -m *": [ "exec" ]
+                       },
                        "ubus": {
                                "luci": [ "setLocaltime", "setPassword" ],
                                "rc": [ "init" ]
git clone https://git.99rst.org/PROJECT