New app: luci-app-sshtunnel for SSH tunnels (#6424)
[project/luci.git] / applications / luci-app-sshtunnel / htdocs / luci-static / resources / view / sshtunnel / ssh_keys.js
1 'use strict';
2 'require form';
3 'require fs';
4 'require ui';
5 'require view';
6
7 var allSshKeys = {};
8 var hasSshKeygen = false;
9
10 return view.extend({
11 load: function () {
12 return L.resolveDefault(fs.list('/root/.ssh/'), []).then(function (entries) {
13 var tasks = [
14 L.resolveDefault(fs.stat('/usr/bin/ssh-keygen'), {}),
15 ];
16 // read pub keys
17 for (var i = 0; i < entries.length; i++) {
18 if (entries[i].type === 'file' && entries[i].name.match(/\.pub$/)) {
19 tasks.push(Promise.resolve(entries[i].name));
20 tasks.push(fs.lines('/root/.ssh/' + entries[i].name));
21 }
22 }
23 return Promise.all(tasks);
24 });
25 },
26
27 render: function (data) {
28 hasSshKeygen = data[0].type === 'file';
29 var sshKeys = _splitSshKeys(data.splice(1));
30
31 var m, s, o;
32
33 m = new form.Map('sshtunnel', _('SSH Tunnels'),
34 _('This configures <a %s>SSH Tunnels</a>')
35 .format('href="https://openwrt.org/docs/guide-user/services/ssh/sshtunnel"')
36 );
37
38 s = m.section(form.GridSection, '_keys');
39 s.render = L.bind(_renderSshKeys, this, sshKeys);
40
41 return m.render();
42 },
43 });
44
45 function _splitSshKeys(sshFiles) {
46 var sshKeys = {};
47 for (var i = 0; i < sshFiles.length; i++) {
48 var sshPubKeyName = sshFiles[i];
49 var sshKeyName = sshPubKeyName.substring(0, sshPubKeyName.length - 4);
50 i++;
51 var sshPub = sshFiles[i];
52 sshKeys[sshKeyName] = '<small><code>' + sshPub + '</code></small>';
53 }
54 allSshKeys = sshKeys;
55 return sshKeys;
56 }
57
58 function _renderSshKeys(sshKeys) {
59 var table = E('table', {'class': 'table cbi-section-table', 'id': 'keys_table'}, [
60 E('tr', {'class': 'tr table-titles'}, [
61 E('th', {'class': 'th'}, _('Name')),
62 E('th', {'class': 'th'}, _('Public Key')),
63 ])
64 ]);
65
66 var rows = Object.entries(sshKeys);
67 cbi_update_table(table, rows, null);
68
69 var keyGenBtn = E('div', {}, [
70 E('form', {
71 'submit': _handleKeyGenSubmit,
72 }, [
73 E('label', {}, _('Generate a new key') + ': '),
74 E('span', {'class': 'control-group'}, [
75 E('input', {
76 'type': 'text',
77 'name': 'keyName',
78 'value': 'id_ed25519',
79 'pattern': '^[a-zA-Z][a-zA-Z0-9_\.]+',
80 'required': 'required',
81 'maxsize': '35',
82 'autocomplete': 'off',
83 }),
84 E('button', {
85 'id': 'btnGenerateKey',
86 'type': 'submit',
87 'class': 'btn cbi-button cbi-button-action',
88 }, [_('Generate')])
89 ])
90 ])
91 ]);
92 return E('div', {'class': 'cbi-section cbi-tblsection'}, [
93 E('h3', _('SSH Keys')),
94 E('div', {'class': 'cbi-section-descr'},
95 _('Add the pub key to %s or %s.')
96 .format('<code>/root/.ssh/authorized_keys</code>', '<code>/etc/dropbear/authorized_keys</code>') + ' ' +
97 _('In LuCI you can do that with <a %s>System / Administration / SSH-Keys</a>')
98 .format('href="/cgi-bin/luci/admin/system/admin/sshkeys"')
99 ),
100 keyGenBtn, table
101 ]);
102 }
103
104 function _handleKeyGenSubmit(event) {
105 event.preventDefault();
106 var keyName = document.querySelector('input[name="keyName"]').value;
107 if (allSshKeys[keyName]) {
108 document.body.scrollTop = document.documentElement.scrollTop = 0;
109 ui.addNotification(null, E('p', _('A key with that name already exists.'), 'error'));
110 return false;
111 }
112
113 let command = '/usr/bin/ssh-keygen';
114 let commandArgs = ['-t', 'ed25519', '-q', '-N', '', '-f', '/root/.ssh/' + keyName];
115 if (!hasSshKeygen) {
116 command = '/usr/bin/dropbearkey';
117 commandArgs = ['-t', 'ed25519', '-f', '/root/.ssh/' + keyName];
118 }
119 fs.exec(command, commandArgs).then(function (res) {
120 if (res.code === 0) {
121 // refresh the page to see the new key
122 location.reload();
123 } else {
124 throw new Error(res.stdout + ' ' + res.stderr);
125 }
126 }).catch(function (e) {
127 document.body.scrollTop = document.documentElement.scrollTop = 0;
128 ui.addNotification(null, E('p', _('Unable to generate a key: %s').format(e.message)), 'error');
129 });
130 return false;
131 }