2 aclTable: L.cbi.AbstractValue.extend({
3 strGlob: function(pattern, match) {
4 var re = new RegExp('^' + (pattern
5 .replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1')
6 .replace(/\\\*/g, '.*?')) + '$');
11 aclMatch: function(list, group) {
12 for (var i = 0; i < list.length; i++)
14 var x = list[i].replace(/^\s*!\s*/, '');
18 if (this.strGlob(x, group))
22 for (var i = 0; i < list.length; i++)
24 var x = list[i].replace(/^\s*!\s*/, '');
28 if (this.strGlob(x, group))
35 aclTest: function(list, group) {
36 for (var i = 0; i < list.length; i++)
43 aclEqual: function(list1, list2) {
44 if (list1.length != list2.length)
47 for (var i = 0; i < list1.length; i++)
48 if (list1[i] != list2[i])
54 aclFromUCI: function(value) {
56 if (typeof(value) == 'string')
57 list = value.split(/\s+/);
58 else if ($.isArray(value))
65 for (var i = 0; i < this.choices.length; i++)
66 if (this.aclMatch(list, this.choices[i][0]))
67 rv.push(this.choices[i][0]);
72 aclToUCI: function(list) {
73 if (list.length < (this.choices.length / 2))
77 for (var i = 0; i < list.length; i++)
81 for (var i = 0; i < this.choices.length; i++)
82 if (!set[this.choices[i][0]])
83 rv.push('!' + this.choices[i][0]);
88 setAll: function(ev) {
89 $(this).parents('table')
90 .find('input[value=%d]'.format(ev.data.level))
91 .prop('checked', true);
96 var t = $('<table />')
97 .attr('id', this.id(sid))
100 .text(L.tr('ACL Group')))
102 .text(L.trc('No access', 'N'))
103 .attr('title', L.tr('Set all to no access'))
104 .css('cursor', 'pointer')
105 .click({ level: 0 }, this.setAll))
107 .text(L.trc('Read only access', 'R'))
108 .attr('title', L.tr('Set all to read only access'))
109 .css('cursor', 'pointer')
110 .click({ level: 1 }, this.setAll))
112 .text(L.trc('Full access', 'F'))
113 .attr('title', L.tr('Set all to full access'))
114 .css('cursor', 'pointer')
115 .click({ level: 2 }, this.setAll)));
117 var acl_r = this.aclFromUCI(this.map.get('rpcd', sid, 'read'));
118 var acl_w = this.aclFromUCI(this.map.get('rpcd', sid, 'write'));
121 for (var i = 0; i < this.choices.length; i++)
123 var r = t.get(0).insertRow(-1);
124 var is_r = this.aclTest(acl_r, this.choices[i][0]);
125 var is_w = this.aclTest(acl_w, this.choices[i][0]);
128 .text(this.choices[i][1]);
130 for (var j = 0; j < 3; j++)
133 .append($('<input />')
134 .attr('type', 'radio')
135 .attr('name', '%s_%s'.format(this.id(sid), this.choices[i][0]))
137 .prop('checked', (j == 0 && !is_r && !is_w) ||
138 (j == 1 && is_r && !is_w) ||
146 textvalue: function(sid)
148 var acl_r = this.aclFromUCI(this.map.get('rpcd', sid, 'read'));
149 var acl_w = this.aclFromUCI(this.map.get('rpcd', sid, 'write'));
151 var htmlid = this.id(sid);
152 var radios = $('#' + htmlid + ' input');
156 for (var i = 0; i < this.choices.length; i++)
158 switch (radios.filter('[name=%s_%s]:checked'.format(htmlid, this.choices[i][0])).val())
161 acls.push('%s: %s'.format(this.choices[i][0], L.trc('Full access', 'F')));
165 acls.push('%s: %s'.format(this.choices[i][0], L.trc('Read only access', 'R')));
169 acls.push('%s: %s'.format(this.choices[i][0], L.trc('No access', 'N')));
174 return acls.join(', ');
177 value: function(k, v)
182 this.choices.push([k, v || k]);
188 var acl_r = this.aclFromUCI(this.map.get('rpcd', sid, 'read'));
189 var acl_w = this.aclFromUCI(this.map.get('rpcd', sid, 'write'));
194 var htmlid = this.id(sid);
195 var radios = $('#' + htmlid + ' input');
197 for (var i = 0; i < this.choices.length; i++)
199 switch (radios.filter('[name=%s_%s]:checked'.format(htmlid, this.choices[i][0])).val())
202 acl_r_new.push(this.choices[i][0]);
203 acl_w_new.push(this.choices[i][0]);
207 acl_r_new.push(this.choices[i][0]);
212 if (!this.aclEqual(acl_r, acl_r_new))
213 this.map.set('rpcd', sid, 'read', this.aclToUCI(acl_r_new));
215 if (!this.aclEqual(acl_w, acl_w_new))
216 this.map.set('rpcd', sid, 'write', this.aclToUCI(acl_w_new));
220 execute: function() {
222 L.ui.listAvailableACLs().then(function(acls) {
223 var m = new L.cbi.Map('rpcd', {
224 caption: L.tr('Guest Logins'),
225 description: L.tr('Manage user accounts and permissions for accessing the LuCI ui.'),
226 readonly: !self.options.acls.users
229 var s = m.section(L.cbi.TypedSection, 'login', {
230 caption: function(sid) {
231 var u = sid ? this.fields.username.textvalue(sid) : undefined;
232 return u ? L.tr('Login "%s"').format(u) : L.tr('New login');
236 add_caption: L.tr('Add new user …'),
237 teasers: [ '__shadow', '__acls' ]
240 s.option(L.cbi.InputValue, 'username', {
241 caption: L.tr('Username'),
242 description: L.tr('Specifies the login name for the guest account'),
247 var shadow = s.option(L.cbi.CheckboxValue, '__shadow', {
248 caption: L.tr('Use system account'),
249 description: L.tr('Use password from the Linux user database')
252 shadow.ucivalue = function(sid) {
253 var pw = this.map.get('rpcd', sid, 'password');
254 return (pw && pw.indexOf('$p$') == 0);
258 var password = s.option(L.cbi.PasswordValue, 'password', {
259 caption: L.tr('Password'),
260 description: L.tr('Specifies the password for the guest account. If you enter a plaintext password here, it will get replaced with a crypted password hash on save.'),
264 password.depends('__shadow', false);
266 password.toggle = function(sid) {
267 var id = '#' + this.id(sid);
268 var pw = this.map.get('rpcd', sid, 'password');
269 var sh = this.section.fields.__shadow.formvalue(sid);
271 if (!sh && pw && pw.indexOf('$p$') == 0)
274 this.callSuper('toggle', sid);
277 shadow.save = password.save = function(sid) {
278 var sh = this.section.fields.__shadow.formvalue(sid);
279 var pw = this.section.fields.password.formvalue(sid);
285 pw = '$p$' + this.section.fields.username.formvalue(sid);
287 if (pw.match(/^\$[0-9p][a-z]?\$/))
289 if (pw != this.map.get('rpcd', sid, 'password'))
290 this.map.set('rpcd', sid, 'password', pw);
295 return L.ui.cryptPassword(pw).then(function(crypt) {
296 map.set('rpcd', sid, 'password', crypt);
301 var o = s.option(self.aclTable, '__acls', {
302 caption: L.tr('User ACLs'),
303 description: L.tr('Specifies the access levels of this account. The "N" column means no access, "R" stands for read only access and "F" for full access.')
307 for (var group_name in acls)
308 groups.push(group_name);
312 for (var i = 0; i < groups.length; i++)
313 o.value(groups[i], acls[groups[i]].description);
315 return m.insertInto('#map');