luci-mod-system: give repokeys the ability to fetch keys from a remote URL
authorPaul Donald <[email protected]>
Sat, 25 Oct 2025 20:46:20 +0000 (22:46 +0200)
committerPaul Donald <[email protected]>
Sat, 25 Oct 2025 20:46:20 +0000 (22:46 +0200)
Simply paste in an http(s) URL e.g.

https://example.com/releases/SNAPSHOT/publickey.pub

and press Add Key.

Signed-off-by: Paul Donald <[email protected]>
modules/luci-mod-system/htdocs/luci-static/resources/view/system/repokeys.js

index 2b1b757198ce2e60679b24d3ab86cf13337cabdb..5c059ac689b7da950d76df515a3c958219f93a8a 100644 (file)
@@ -135,11 +135,50 @@ function keyEnvironmentCheck(key) {
 function addKey(ev, file, fileContent) {
        const list = findParent(ev.target, '.cbi-dynlist');
        const input = list.querySelector('textarea[type="text"]');
-       const key = input.value;
+       let key = (fileContent ?? input.value.trim());
 
        if (!key.length)
                return;
 
+       // Handle remote URL paste
+       if (/^https?:\/\/\S+$/i.test(key) && !fileContent) {
+               ui.addTimeLimitedNotification(_('Fetching key from URL…'), [], 5000, 'info');
+
+               L.Request.request(key, { method: 'GET' }).then(res => {
+                       if (res.status !== 200) {
+                               ui.addTimeLimitedNotification(_('Failed to fetch key'), [
+                                       E('p', _('HTTP error %d').format(res.status)),
+                               ], 7000, 'warning');
+                               return;
+                       }
+
+                       const fetched = res.responseText?.trim();
+                       if (!fetched || fetched.length > 8192) {
+                               ui.addTimeLimitedNotification(_('Key file too large'), [
+                                       E('p', _('Fetched content seems too long. Maximum 8192 bytes.')),
+                               ], 7000, 'warning');
+                               return;
+                       }
+
+                       if (!fetched || fetched.length < 32) {
+                               ui.addTimeLimitedNotification(_('Invalid or empty key file'), [
+                                       E('p', _('Fetched content seems empty or too short.')),
+                               ], 7000, 'warning');
+                               return;
+                       }
+
+                       // Continue directly with fetched key
+                       addKey(ev, null, fetched);
+               }).catch(err => {
+                       ui.addTimeLimitedNotification(_('Failed to fetch key'), [
+                               E('p', err.message),
+                       ], 7000, 'warning');
+               });
+
+               return;
+       }
+
+       // From here on, key content (either pasted, fetched, or dropped)
        const formatError = keyEnvironmentCheck(key);
        if (formatError) {
                ui.addTimeLimitedNotification(_('Invalid key format'), [
@@ -211,7 +250,8 @@ return view.extend({
                                        'aria-label': _('Paste or drag repository public key'),
                                        class: 'cbi-input-text',
                                        type: 'text',
-                                       placeholder: _('Paste or drag to upload a software repository public key…'),
+                                       style: 'width: 300px; min-height: 120px; ',
+                                       placeholder: _('Paste content of a file, or a URL to a key file, or drag and drop here to upload a software repository public key…'),
                                        keydown: function(ev) { if (ev.keyCode === 13) addKey(ev); },
                                        disabled: isReadonlyView
                                }),