L.ui.view.extend({
aclTable: L.cbi.AbstractValue.extend({
strGlob: function(pattern, match) {
var re = new RegExp('^' + (pattern
.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1')
.replace(/\\\*/g, '.*?')) + '$');
return re.test(match);
},
aclMatch: function(list, group) {
for (var i = 0; i < list.length; i++)
{
var x = list[i].replace(/^\s*!\s*/, '');
if (x == list[i])
continue;
if (this.strGlob(x, group))
return false;
}
for (var i = 0; i < list.length; i++)
{
var x = list[i].replace(/^\s*!\s*/, '');
if (x != list[i])
continue;
if (this.strGlob(x, group))
return true;
}
return false;
},
aclTest: function(list, group) {
for (var i = 0; i < list.length; i++)
if (list[i] == group)
return true;
return false;
},
aclEqual: function(list1, list2) {
if (list1.length != list2.length)
return false;
for (var i = 0; i < list1.length; i++)
if (list1[i] != list2[i])
return false;
return true;
},
aclFromUCI: function(value) {
var list;
if (typeof(value) == 'string')
list = value.split(/\s+/);
else if ($.isArray(value))
list = value;
else
list = [ ];
var rv = [ ];
if (this.choices)
for (var i = 0; i < this.choices.length; i++)
if (this.aclMatch(list, this.choices[i][0]))
rv.push(this.choices[i][0]);
return rv;
},
aclToUCI: function(list) {
if (list.length < (this.choices.length / 2))
return list;
var set = { };
for (var i = 0; i < list.length; i++)
set[list[i]] = true;
var rv = [ '*' ];
for (var i = 0; i < this.choices.length; i++)
if (!set[this.choices[i][0]])
rv.push('!' + this.choices[i][0]);
return rv;
},
setAll: function(ev) {
$(this).parents('table')
.find('input[value=%d]'.format(ev.data.level))
.prop('checked', true);
},
widget: function(sid)
{
var t = $('
')
.attr('id', this.id(sid))
.append($('
')
.append($('
')
.text(L.tr('ACL Group')))
.append($('
')
.text(L.trc('No access', 'N'))
.attr('title', L.tr('Set all to no access'))
.css('cursor', 'pointer')
.click({ level: 0 }, this.setAll))
.append($('
')
.text(L.trc('Read only access', 'R'))
.attr('title', L.tr('Set all to read only access'))
.css('cursor', 'pointer')
.click({ level: 1 }, this.setAll))
.append($('
')
.text(L.trc('Full access', 'F'))
.attr('title', L.tr('Set all to full access'))
.css('cursor', 'pointer')
.click({ level: 2 }, this.setAll)));
var acl_r = this.aclFromUCI(this.map.get('rpcd', sid, 'read'));
var acl_w = this.aclFromUCI(this.map.get('rpcd', sid, 'write'));
if (this.choices)
for (var i = 0; i < this.choices.length; i++)
{
var r = t.get(0).insertRow(-1);
var is_r = this.aclTest(acl_r, this.choices[i][0]);
var is_w = this.aclTest(acl_w, this.choices[i][0]);
$(r.insertCell(-1))
.text(this.choices[i][1]);
for (var j = 0; j < 3; j++)
{
$(r.insertCell(-1))
.append($('')
.attr('type', 'radio')
.attr('name', '%s_%s'.format(this.id(sid), this.choices[i][0]))
.attr('value', j)
.prop('checked', (j == 0 && !is_r && !is_w) ||
(j == 1 && is_r && !is_w) ||
(j == 2 && is_w)));
}
}
return t;
},
textvalue: function(sid)
{
var acl_r = this.aclFromUCI(this.map.get('rpcd', sid, 'read'));
var acl_w = this.aclFromUCI(this.map.get('rpcd', sid, 'write'));
var htmlid = this.id(sid);
var radios = $('#' + htmlid + ' input');
var acls = [ ];
for (var i = 0; i < this.choices.length; i++)
{
switch (radios.filter('[name=%s_%s]:checked'.format(htmlid, this.choices[i][0])).val())
{
case '2':
acls.push('%s: %s'.format(this.choices[i][0], L.trc('Full access', 'F')));
break;
case '1':
acls.push('%s: %s'.format(this.choices[i][0], L.trc('Read only access', 'R')));
break;
case '0':
acls.push('%s: %s'.format(this.choices[i][0], L.trc('No access', 'N')));
break;
}
}
return acls.join(', ');
},
value: function(k, v)
{
if (!this.choices)
this.choices = [ ];
this.choices.push([k, v || k]);
return this;
},
save: function(sid)
{
var acl_r = this.aclFromUCI(this.map.get('rpcd', sid, 'read'));
var acl_w = this.aclFromUCI(this.map.get('rpcd', sid, 'write'));
var acl_r_new = [ ];
var acl_w_new = [ ];
var htmlid = this.id(sid);
var radios = $('#' + htmlid + ' input');
for (var i = 0; i < this.choices.length; i++)
{
switch (radios.filter('[name=%s_%s]:checked'.format(htmlid, this.choices[i][0])).val())
{
case '2':
acl_r_new.push(this.choices[i][0]);
acl_w_new.push(this.choices[i][0]);
break;
case '1':
acl_r_new.push(this.choices[i][0]);
break;
}
}
if (!this.aclEqual(acl_r, acl_r_new))
this.map.set('rpcd', sid, 'read', this.aclToUCI(acl_r_new));
if (!this.aclEqual(acl_w, acl_w_new))
this.map.set('rpcd', sid, 'write', this.aclToUCI(acl_w_new));
}
}),
execute: function() {
var self = this;
L.ui.listAvailableACLs().then(function(acls) {
var m = new L.cbi.Map('rpcd', {
caption: L.tr('Guest Logins'),
description: L.tr('Manage user accounts and permissions for accessing the LuCI ui.'),
readonly: !self.options.acls.users
});
var s = m.section(L.cbi.TypedSection, 'login', {
caption: function(sid) {
var u = sid ? this.fields.username.textvalue(sid) : undefined;
return u ? L.tr('Login "%s"').format(u) : L.tr('New login');
},
collabsible: true,
addremove: true,
add_caption: L.tr('Add new user …'),
teasers: [ '__shadow', '__acls' ]
});
s.option(L.cbi.InputValue, 'username', {
caption: L.tr('Username'),
description: L.tr('Specifies the login name for the guest account'),
optional: false
});
var shadow = s.option(L.cbi.CheckboxValue, '__shadow', {
caption: L.tr('Use system account'),
description: L.tr('Use password from the Linux user database')
});
shadow.ucivalue = function(sid) {
var pw = this.map.get('rpcd', sid, 'password');
return (pw && pw.indexOf('$p$') == 0);
};
var password = s.option(L.cbi.PasswordValue, 'password', {
caption: L.tr('Password'),
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.'),
optional: false
});
password.depends('__shadow', false);
password.toggle = function(sid) {
var id = '#' + this.id(sid);
var pw = this.map.get('rpcd', sid, 'password');
var sh = this.section.fields.__shadow.formvalue(sid);
if (!sh && pw && pw.indexOf('$p$') == 0)
$(id).val('');
this.callSuper('toggle', sid);
};
shadow.save = password.save = function(sid) {
var sh = this.section.fields.__shadow.formvalue(sid);
var pw = this.section.fields.password.formvalue(sid);
if (!sh && !pw)
return;
if (sh)
pw = '$p$' + this.section.fields.username.formvalue(sid);
if (pw.match(/^\$[0-9p][a-z]?\$/))
{
if (pw != this.map.get('rpcd', sid, 'password'))
this.map.set('rpcd', sid, 'password', pw);
}
else
{
var map = this.map;
return L.ui.cryptPassword(pw).then(function(crypt) {
map.set('rpcd', sid, 'password', crypt);
});
}
};
var o = s.option(self.aclTable, '__acls', {
caption: L.tr('User ACLs'),
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.')
});
var groups = [ ];
for (var group_name in acls)
groups.push(group_name);
groups.sort();
for (var i = 0; i < groups.length; i++)
o.value(groups[i], acls[groups[i]].description);
return m.insertInto('#map');
});
}
});