Translated using Weblate (Polish)
[project/luci.git] / modules / luci-mod-system / htdocs / luci-static / resources / view / system / sshkeys.js
index 05d41b0dde49d1ae3ec4bbd5341c75170edad861..a1748ad9907ea23c49e0cfbd231ca3f59be1fa83 100644 (file)
@@ -1,7 +1,12 @@
 'use strict';
+'require baseclass';
+'require view';
 'require fs';
+'require ui';
 
-var SSHPubkeyDecoder = L.Class.singleton({
+var isReadonlyView = !L.hasViewPermission() || null;
+
+var SSHPubkeyDecoder = baseclass.singleton({
        lengthDecode: function(s, off)
        {
                var l = (s.charCodeAt(off++) << 24) |
@@ -17,12 +22,13 @@ var SSHPubkeyDecoder = L.Class.singleton({
 
        decode: function(s)
        {
-               var parts = s.split(/\s+/);
-               if (parts.length < 2)
+               var parts = s.trim().match(/^((?:(?:^|,)[^ =,]+(?:=(?:[^ ",]+|"(?:[^"\\]|\\.)*"))?)+ +)?(ssh-dss|ssh-rsa|ssh-ed25519|ecdsa-sha2-nistp[0-9]+|sk-ecdsa-sha2-nistp256@openssh\.com|sk-ssh-ed25519@openssh\.com) +([^ ]+)( +.*)?$/);
+
+               if (!parts)
                        return null;
 
                var key = null;
-               try { key = atob(parts[1]); } catch(e) {}
+               try { key = atob(parts[3]); } catch(e) {}
                if (!key)
                        return null;
 
@@ -35,7 +41,7 @@ var SSHPubkeyDecoder = L.Class.singleton({
                        return null;
 
                var type = key.substr(off + 4, len);
-               if (type !== parts[0])
+               if (type !== parts[2])
                        return null;
 
                off += 4 + len;
@@ -67,22 +73,38 @@ var SSHPubkeyDecoder = L.Class.singleton({
                if (len2 & 1)
                        len2--;
 
-               var comment = parts.slice(2).join(' '),
-                   fprint = parts[1].length > 68 ? parts[1].substr(0, 33) + '…' + parts[1].substr(-34) : parts[1];
+               var comment = (parts[4] || '').trim(),
+                   fprint = parts[3].length > 68 ? parts[3].substr(0, 33) + '…' + parts[3].substr(-34) : parts[3];
+
+               var options = null;
+               (parts[1] || '').trim().replace(/(?:^|,)([^ =,]+)(?:=(?:([^ ",]+)|"((?:[^"\\]|\\.)*)"))?/g, function(m, k, p, q) {
+                       options = options || {};
+
+                       if (options.hasOwnProperty(k))
+                               options[k] += ',' + (q || p || true);
+                       else
+                               options[k] = (q || p || true);
+               });
 
                switch (type)
                {
                case 'ssh-rsa':
-                       return { type: 'RSA', bits: len2 * 8, comment: comment, fprint: fprint };
+                       return { type: 'RSA', bits: len2 * 8, comment: comment, options: options, fprint: fprint, src: s };
 
                case 'ssh-dss':
-                       return { type: 'DSA', bits: len1 * 8, comment: comment, fprint: fprint };
+                       return { type: 'DSA', bits: len1 * 8, comment: comment, options: options, fprint: fprint, src: s };
 
                case 'ssh-ed25519':
-                       return { type: 'ECDH', curve: 'Curve25519', comment: comment, fprint: fprint };
+                       return { type: 'EdDSA', curve: 'Curve25519', comment: comment, options: options, fprint: fprint, src: s };
 
                case 'ecdsa-sha2':
-                       return { type: 'ECDSA', curve: curve, comment: comment, fprint: fprint };
+                       return { type: 'ECDSA', curve: curve, comment: comment, options: options, fprint: fprint, src: s };
+               
+               case 'sk-ecdsa-sha2-nistp256@openssh.com':
+                       return { type: 'ECDSA-SK', curve: 'NIST P-256', comment: comment, options: options, fprint: fprint, src: s };
+               
+               case 'sk-ssh-ed25519@openssh.com':
+                       return { type: 'EdDSA-SK', curve: 'Curve25519', comment: comment, options: options, fprint: fprint, src: s };
 
                default:
                        return null;
@@ -90,6 +112,24 @@ var SSHPubkeyDecoder = L.Class.singleton({
        }
 });
 
+function renderKeyItem(pubkey) {
+       return E('div', {
+               class: 'item',
+               click: isReadonlyView ? null : removeKey,
+               'data-key': pubkey.src
+       }, [
+               E('strong', [ pubkey.comment || _('Unnamed key') ]), E('br'),
+               E('small', [
+                       '%s, %s'.format(pubkey.type, pubkey.curve || _('%d Bit').format(pubkey.bits)),
+                       pubkey.options ? E([], [
+                               ' / ', _('Options:'), ' ',
+                               E('code', Object.keys(pubkey.options).sort().join(', '))
+                       ]) : '',
+                       E('br'), E('code', pubkey.fprint)
+               ])
+       ]);
+}
+
 function renderKeys(keys) {
        var list = document.querySelector('.cbi-dynlist');
 
@@ -99,17 +139,7 @@ function renderKeys(keys) {
        keys.forEach(function(key) {
                var pubkey = SSHPubkeyDecoder.decode(key);
                if (pubkey)
-                       list.insertBefore(E('div', {
-                               class: 'item',
-                               click: removeKey,
-                               'data-key': key
-                       }, [
-                               E('strong', pubkey.comment || _('Unnamed key')), E('br'),
-                               E('small', [
-                                       '%s, %s'.format(pubkey.type, pubkey.curve || _('%d Bit').format(pubkey.bits)),
-                                       E('br'), E('code', pubkey.fprint)
-                               ])
-                       ]), list.lastElementChild);
+                       list.insertBefore(renderKeyItem(pubkey), list.lastElementChild);
        });
 
        if (list.firstElementChild === list.lastElementChild)
@@ -119,8 +149,8 @@ function renderKeys(keys) {
 function saveKeys(keys) {
        return fs.write('/etc/dropbear/authorized_keys', keys.join('\n') + '\n', 384 /* 0600 */)
                .then(renderKeys.bind(this, keys))
-               .catch(function(e) { L.ui.addNotification(null, E('p', e.message)) })
-               .finally(L.ui.hideModal);
+               .catch(function(e) { ui.addNotification(null, E('p', e.message)) })
+               .finally(ui.hideModal);
 }
 
 function addKey(ev) {
@@ -138,14 +168,14 @@ function addKey(ev) {
        });
 
        if (keys.indexOf(key) !== -1) {
-               L.ui.showModal(_('Add key'), [
+               ui.showModal(_('Add key'), [
                        E('div', { class: 'alert-message warning' }, _('The given SSH public key has already been added.')),
                        E('div', { class: 'right' }, E('div', { class: 'btn', click: L.hideModal }, _('Close')))
                ]);
        }
        else if (!pubkey) {
-               L.ui.showModal(_('Add key'), [
-                       E('div', { class: 'alert-message warning' }, _('The given SSH public key is invalid. Please supply proper public RSA or ECDSA keys.')),
+               ui.showModal(_('Add key'), [
+                       E('div', { class: 'alert-message warning' }, _('The given SSH public key is invalid. Please supply proper public RSA, ED25519 or ECDSA keys.')),
                        E('div', { class: 'right' }, E('div', { class: 'btn', click: L.hideModal }, _('Close')))
                ]);
        }
@@ -154,7 +184,7 @@ function addKey(ev) {
                input.value = '';
 
                return saveKeys(keys).then(function() {
-                       var added = list.querySelector('[data-key="%s"]'.format(key));
+                       var added = list.querySelector('[data-key="%s"]'.format(key.replace(/["\\]/g, '\\$&')));
                        if (added)
                                added.classList.add('flash');
                });
@@ -174,11 +204,11 @@ function removeKey(ev) {
 
        L.showModal(_('Delete key'), [
                E('div', _('Do you really want to delete the following SSH key?')),
-               E('pre', delkey),
+               E('pre', [ delkey ]),
                E('div', { class: 'right' }, [
                        E('div', { class: 'btn', click: L.hideModal }, _('Cancel')),
                        ' ',
-                       E('div', { class: 'btn danger', click: L.ui.createHandlerFn(this, saveKeys, keys) }, _('Delete key')),
+                       E('div', { class: 'btn danger', click: ui.createHandlerFn(this, saveKeys, keys) }, _('Delete key')),
                ])
        ]);
 }
@@ -212,45 +242,41 @@ function handleWindowDragDropIgnore(ev) {
        ev.preventDefault()
 }
 
-return L.view.extend({
+return view.extend({
        load: function() {
                return fs.lines('/etc/dropbear/authorized_keys').then(function(lines) {
-                       return lines.filter(function(line) {
-                               return line.match(/^ssh-/) != null;
+                       return lines.map(function(line) {
+                               return SSHPubkeyDecoder.decode(line);
+                       }).filter(function(line) {
+                               return line != null;
                        });
                });
        },
 
        render: function(keys) {
-               var list = E('div', { 'class': 'cbi-dynlist', 'dragover': dragKey, 'drop': dropKey }, [
+               var list = E('div', {
+                       'class': 'cbi-dynlist',
+                       'dragover': isReadonlyView ? null : dragKey,
+                       'drop': isReadonlyView ? null : dropKey
+               }, [
                        E('div', { 'class': 'add-item' }, [
                                E('input', {
                                        'class': 'cbi-input-text',
                                        'type': 'text',
                                        'placeholder': _('Paste or drag SSH key file…') ,
-                                       'keydown': function(ev) { if (ev.keyCode === 13) addKey(ev) }
+                                       'keydown': function(ev) { if (ev.keyCode === 13) addKey(ev) },
+                                       'disabled': isReadonlyView
                                }),
                                E('button', {
                                        'class': 'cbi-button',
-                                       'click': L.ui.createHandlerFn(this, addKey)
+                                       'click': ui.createHandlerFn(this, addKey),
+                                       'disabled': isReadonlyView
                                }, _('Add key'))
                        ])
                ]);
 
-               keys.forEach(L.bind(function(key) {
-                       var pubkey = SSHPubkeyDecoder.decode(key);
-                       if (pubkey)
-                               list.insertBefore(E('div', {
-                                       class: 'item',
-                                       click: L.ui.createHandlerFn(this, removeKey),
-                                       'data-key': key
-                               }, [
-                                       E('strong', pubkey.comment || _('Unnamed key')), E('br'),
-                                       E('small', [
-                                               '%s, %s'.format(pubkey.type, pubkey.curve || _('%d Bit').format(pubkey.bits)),
-                                               E('br'), E('code', pubkey.fprint)
-                                       ])
-                               ]), list.lastElementChild);
+               keys.forEach(L.bind(function(pubkey) {
+                       list.insertBefore(renderKeyItem(pubkey), list.lastElementChild);
                }, this));
 
                if (list.firstElementChild === list.lastElementChild)