2 PubkeyListValue: L.cbi.AbstractValue.extend({
4 'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6,
5 'H': 7, 'I': 8, 'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13,
6 'O': 14, 'P': 15, 'Q': 16, 'R': 17, 'S': 18, 'T': 19, 'U': 20,
7 'V': 21, 'W': 22, 'X': 23, 'Y': 24, 'Z': 25, 'a': 26, 'b': 27,
8 'c': 28, 'd': 29, 'e': 30, 'f': 31, 'g': 32, 'h': 33, 'i': 34,
9 'j': 35, 'k': 36, 'l': 37, 'm': 38, 'n': 39, 'o': 40, 'p': 41,
10 'q': 42, 'r': 43, 's': 44, 't': 45, 'u': 46, 'v': 47, 'w': 48,
11 'x': 49, 'y': 50, 'z': 51, '0': 52, '1': 53, '2': 54, '3': 55,
12 '4': 56, '5': 57, '6': 58, '7': 59, '8': 60, '9': 61, '+': 62,
16 base64Decode: function(s)
21 if (s.match(/[^A-Za-z0-9\+\/\=]/))
26 var e1 = this.base64Table[s.charAt(i++)];
27 var e2 = this.base64Table[s.charAt(i++)];
28 var e3 = this.base64Table[s.charAt(i++)];
29 var e4 = this.base64Table[s.charAt(i++)];
31 var c1 = ( e1 << 2) | (e2 >> 4);
32 var c2 = ((e2 & 15) << 4) | (e3 >> 2);
33 var c3 = ((e3 & 3) << 6) | e4;
35 d += String.fromCharCode(c1);
38 d += String.fromCharCode(c2);
41 d += String.fromCharCode(c3);
47 lengthDecode: function(s, off)
49 var l = (s.charCodeAt(off++) << 24) |
50 (s.charCodeAt(off++) << 16) |
51 (s.charCodeAt(off++) << 8) |
54 if (l < 0 || (off + l) > s.length)
60 pubkeyDecode: function(s)
62 var parts = s.split(/\s+/);
66 var key = this.base64Decode(parts[1]);
73 len = this.lengthDecode(key, off);
78 var type = key.substr(off + 4, len);
84 var len1 = this.lengthDecode(key, off);
90 var len2 = this.lengthDecode(key, off);
103 return { type: 'RSA', bits: len2 * 8, comment: parts[2] };
106 return { type: 'DSA', bits: len1 * 8, comment: parts[2] };
113 _remove: function(ev)
115 var self = ev.data.self;
117 self._keys.splice(ev.data.index, 1);
118 self._render(ev.data.div);
123 var self = ev.data.self;
125 var form = $('<div />')
127 .text(L.tr('Paste the public key line into the field below and press "%s" to continue.').format(L.tr('Ok'))))
129 .text(L.tr('Unrecognized public key! Please add only RSA or DSA keys.'))
130 .addClass('alert alert-danger')
133 .append($('<input />')
134 .attr('type', 'text')
135 .attr('placeholder', L.tr('Paste key here'))
136 .addClass('form-control')));
138 L.ui.dialog(L.tr('Add new public key'), form, {
140 confirm: function() {
141 var val = form.find('input').val();
147 var key = self.pubkeyDecode(val);
150 form.find('input').val('');
151 form.find('.alert').show();
155 self._keys.push(val);
156 self._render(ev.data.div);
165 var self = ev.data.self;
169 $('<pre />').text(self._keys[ev.data.index]),
174 _render: function(div)
178 for (var i = 0; i < this._keys.length; i++)
180 var k = this.pubkeyDecode(this._keys[i] || '');
186 .addClass('input-group')
187 .append($('<input />')
188 .addClass('form-control')
189 .attr('type', 'text')
190 .prop('readonly', true)
191 .click({ self: this, index: i }, this._show)
192 .val('%dBit %s - %s'.format(k.bits, k.type, k.comment || '?')))
193 .append($('<span />')
194 .addClass('input-group-btn')
195 .append($('<button />')
196 .addClass('btn btn-danger')
197 .attr('title', L.tr('Remove public key'))
199 .click({ self: this, div: div, index: i }, this._remove)))
203 if (this._keys.length > 0)
204 $('<br />').appendTo(div);
206 L.ui.button(L.tr('Add public key …'), 'success')
207 .click({ self: this, div: div }, this._add)
211 widget: function(sid)
215 for (var i = 0; i < this.options.keys.length; i++)
216 this._keys.push(this.options.keys[i]);
219 .attr('id', this.id(sid));
226 changed: function(sid)
228 if (this.options.keys.length != this._keys.length)
231 for (var i = 0; i < this.options.keys.length; i++)
232 if (this.options.keys[i] != this._keys[i])
240 if (this.changed(sid))
242 this.options.keys = [ ];
244 for (var i = 0; i < this._keys.length; i++)
245 this.options.keys.push(this._keys[i]);
247 return L.system.setSSHKeys(this._keys);
254 execute: function() {
256 return L.system.getSSHKeys().then(function(keys) {
257 var m = new L.cbi.Map('dropbear', {
258 caption: L.tr('SSH Access'),
259 description: L.tr('Dropbear offers SSH network shell access and an integrated SCP server'),
263 var s1 = m.section(L.cbi.DummySection, '__password', {
264 caption: L.tr('Router Password'),
265 description: L.tr('Changes the administrator password for accessing the device'),
266 readonly: !self.options.acls.admin
269 var p1 = s1.option(L.cbi.PasswordValue, 'pass1', {
270 caption: L.tr('Password'),
274 var p2 = s1.option(L.cbi.PasswordValue, 'pass2', {
275 caption: L.tr('Confirmation'),
277 datatype: function(v) {
278 var v1 = p1.formvalue('__password');
279 if (v1 && v1.length && v != v1)
280 return L.tr('Passwords must match!');
285 p1.save = function(sid) { };
286 p2.save = function(sid) {
287 var v1 = p1.formvalue(sid);
288 var v2 = p2.formvalue(sid);
289 if (v2 && v2.length > 0 && v1 == v2)
290 return L.system.setPassword('root', v2);
294 var s2 = m.section(L.cbi.DummySection, '__pubkeys', {
295 caption: L.tr('SSH-Keys'),
296 description: L.tr('Specifies public keys for passwordless SSH authentication'),
297 readonly: !self.options.acls.admin
300 var k = s2.option(self.PubkeyListValue, 'keys', {
301 caption: L.tr('Saved keys'),
306 var s3 = m.section(L.cbi.TypedSection, 'dropbear', {
307 caption: L.tr('SSH Server'),
308 description: L.tr('This sections define listening instances of the builtin Dropbear SSH server'),
310 add_caption: L.tr('Add instance ...'),
311 readonly: !self.options.acls.admin,
315 s3.option(L.cbi.NetworkList, 'Interface', {
316 caption: L.tr('Interface'),
317 description: L.tr('Listen only on the given interface or, if unspecified, on all')
320 s3.option(L.cbi.InputValue, 'Port', {
321 caption: L.tr('Port'),
322 description: L.tr('Specifies the listening port of this Dropbear instance'),
328 s3.option(L.cbi.CheckboxValue, 'PasswordAuth', {
329 caption: L.tr('Password authentication'),
330 description: L.tr('Allow SSH password authentication'),
336 s3.option(L.cbi.CheckboxValue, 'RootPasswordAuth', {
337 caption: L.tr('Allow root logins with password'),
338 description: L.tr('Allow the root user to login with password'),
344 s3.option(L.cbi.CheckboxValue, 'GatewayPorts', {
345 caption: L.tr('Gateway ports'),
346 description: L.tr('Allow remote hosts to connect to local SSH forwarded ports'),
352 return m.insertInto('#map');